From ee12c3d60c4bb7b25e06709e92344d2d8b2c581e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 9 Apr 2012 23:50:56 +0200 Subject: [PATCH] Use filesystem::path instead of manual string tinkering Where possible, use boost::filesystem::path instead of std::string or char* for filenames. This avoids a lot of manual string tinkering, in favor of path::operator/. GetDataDir is also reworked significantly, it now only keeps two cached directory names (the network-specific data dir, and the root data dir), which are decided through a parameter instead of pre-initialized global variables. Finally, remove the "upgrade from 0.1.5" case where a debug.log in the current directory has to be removed. --- src/bitcoinrpc.cpp | 8 +-- src/db.cpp | 22 +++---- src/init.cpp | 36 +++++------- src/main.cpp | 2 +- src/util.cpp | 142 +++++++++++++++++++-------------------------- src/util.h | 16 ++--- 6 files changed, 95 insertions(+), 131 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index c8955263..2525c2d5 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2364,7 +2364,7 @@ void ThreadRPCServer2(void* parg) "(you do not need to remember this password)\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), - GetConfigFile().c_str(), + GetConfigFile().string().c_str(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), _("Error"), wxOK | wxMODAL); QueueShutdown(); @@ -2399,12 +2399,12 @@ void ThreadRPCServer2(void* parg) filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; - if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string().c_str()); + if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; - if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string().c_str(), ssl::context::pem); + if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); @@ -2544,7 +2544,7 @@ Object CallRPC(const string& strMethod, const Array& params) throw runtime_error(strprintf( _("You must set rpcpassword= in the configuration file:\n%s\n" "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().c_str())); + GetConfigFile().string().c_str())); // Connect to localhost bool fUseSSL = GetBoolArg("-rpcssl"); diff --git a/src/db.cpp b/src/db.cpp index ceae47fb..839c0807 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -43,7 +43,7 @@ static void EnvShutdown() { printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); } - DbEnv(0).remove(GetDataDir().c_str(), 0); + DbEnv(0).remove(GetDataDir().string().c_str(), 0); } class CDBInit @@ -60,7 +60,7 @@ public: instance_of_cdbinit; -CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) +CDB::CDB(const char *pszFile, const char* pszMode) : pdb(NULL) { int ret; if (pszFile == NULL) @@ -78,10 +78,10 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) { if (fShutdown) return; - string strDataDir = GetDataDir(); - filesystem::path pathLogDir(strDataDir + "/database"); + filesystem::path pathDataDir = GetDataDir(); + filesystem::path pathLogDir = pathDataDir / "database"; filesystem::create_directory(pathLogDir); - filesystem::path pathErrorFile(strDataDir + "/db.log"); + filesystem::path pathErrorFile = pathDataDir / "db.log"; printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); int nDbCache = GetArg("-dbcache", 25); @@ -94,7 +94,7 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - ret = dbenv.open(strDataDir.c_str(), + ret = dbenv.open(pathDataDir.string().c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -1087,13 +1087,7 @@ int CWalletDB::LoadWallet(CWallet* pwallet) return DB_NEED_REWRITE; if (nFileVersion < CLIENT_VERSION) // Update - { - // Get rid of old debug.log file in current directory - if (nFileVersion <= 105 && !pszSetDataDir[0]) - unlink("debug.log"); - WriteVersion(CLIENT_VERSION); - } return DB_LOAD_OK; } @@ -1176,10 +1170,10 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); + filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; filesystem::path pathDest(strDest); if (filesystem::is_directory(pathDest)) - pathDest = pathDest / wallet.strWalletFile; + pathDest /= wallet.strWalletFile; try { #if BOOST_VERSION >= 104000 diff --git a/src/init.cpp b/src/init.cpp index 8d338781..2be0027a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -307,11 +307,11 @@ bool AppInit2(int argc, char* argv[]) } #endif - if (!fDebug && !pszSetDataDir[0]) + if (!fDebug) ShrinkDebugFile(); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); - printf("Default data directory %s\n", GetDefaultDataDir().c_str()); + printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); if (GetBoolArg("-loadblockindextest")) { @@ -322,13 +322,13 @@ bool AppInit2(int argc, char* argv[]) } // Make sure only a single bitcoin process is using the data directory. - string strLockFile = GetDataDir() + "/.lock"; - FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist. + boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; + FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); - static boost::interprocess::file_lock lock(strLockFile.c_str()); + static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) { - ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().c_str()), _("Bitcoin"), wxOK|wxMODAL); + ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str()), _("Bitcoin"), wxOK|wxMODAL); return false; } @@ -584,20 +584,20 @@ bool AppInit2(int argc, char* argv[]) } #ifdef WIN32 -string StartupShortcutPath() +boost::filesystem::path StartupShortcutPath() { - return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk"; + return MyGetSpecialFolderPath(CSIDL_STARTUP, true) / "Bitcoin.lnk"; } bool GetStartOnSystemStartup() { - return filesystem::exists(StartupShortcutPath().c_str()); + return filesystem::exists(StartupShortcutPath()); } bool SetStartOnSystemStartup(bool fAutoStart) { // If the shortcut exists already, remove it for updating - remove(StartupShortcutPath().c_str()); + boost::filesystem::remove(StartupShortcutPath()); if (fAutoStart) { @@ -633,7 +633,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) { WCHAR pwsz[MAX_PATH]; // Ensure that the string is ANSI. - MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH); + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); // Save the link by calling IPersistFile::Save. hres = ppf->Save(pwsz, TRUE); ppf->Release(); @@ -659,15 +659,15 @@ boost::filesystem::path GetAutostartDir() namespace fs = boost::filesystem; char* pszConfigHome = getenv("XDG_CONFIG_HOME"); - if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; char* pszHome = getenv("HOME"); - if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; return fs::path(); } boost::filesystem::path GetAutostartFilePath() { - return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop"); + return GetAutostartDir() / "bitcoin.desktop"; } bool GetStartOnSystemStartup() @@ -692,13 +692,7 @@ bool GetStartOnSystemStartup() bool SetStartOnSystemStartup(bool fAutoStart) { if (!fAutoStart) - { -#if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3 - unlink(GetAutostartFilePath().string().c_str()); -#else - unlink(GetAutostartFilePath().native_file_string().c_str()); -#endif - } + boost::filesystem::remove(GetAutostartFilePath()); else { char pszExePath[MAX_PATH+1]; diff --git a/src/main.cpp b/src/main.cpp index 3fe07324..b9c9db7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1822,7 +1822,7 @@ FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszM { if (nFile == -1) return NULL; - FILE* file = fopen(strprintf("%s/blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode); + FILE* file = fopen((GetDataDir() / strprintf("blk%04d.dat", nFile)).string().c_str(), pszMode); if (!file) return NULL; if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) diff --git a/src/util.cpp b/src/util.cpp index 6c007c11..91f1810e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -22,7 +22,6 @@ map > mapMultiArgs; bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; -char pszSetDataDir[MAX_PATH] = ""; bool fRequestShutdown = false; bool fShutdown = false; bool fDaemon = false; @@ -165,10 +164,8 @@ inline int OutputDebugStringF(const char* pszFormat, ...) if (!fileout) { - char pszFile[MAX_PATH+100]; - GetDataDir(pszFile); - strlcat(pszFile, "/debug.log", sizeof(pszFile)); - fileout = fopen(pszFile, "a"); + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); if (fileout) setbuf(fileout, NULL); // unbuffered } if (fileout) @@ -768,101 +765,94 @@ void PrintExceptionContinue(std::exception* pex, const char* pszThread) } #ifdef WIN32 -string MyGetSpecialFolderPath(int nFolder, bool fCreate) +boost::filesystem::path MyGetSpecialFolderPath(int nFolder, bool fCreate) { + namespace fs = boost::filesystem; + char pszPath[MAX_PATH] = ""; if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) { - return pszPath; + return fs::path(pszPath); } else if (nFolder == CSIDL_STARTUP) { - return string(getenv("USERPROFILE")) + "\\Start Menu\\Programs\\Startup"; + return fs::path(getenv("USERPROFILE")) / "Start Menu" / "Programs" / "Startup"; } else if (nFolder == CSIDL_APPDATA) { - return getenv("APPDATA"); + return fs::path(getenv("APPDATA")); } - return ""; + return fs::path(""); } #endif -string GetDefaultDataDir() +boost::filesystem::path GetDefaultDataDir() { + namespace fs = boost::filesystem; + // Windows: C:\Documents and Settings\username\Application Data\Bitcoin // Mac: ~/Library/Application Support/Bitcoin // Unix: ~/.bitcoin #ifdef WIN32 // Windows - return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin"; + return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "Bitcoin"; #else + fs::path pathRet; char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) - pszHome = (char*)"/"; - string strHome = pszHome; - if (strHome[strHome.size()-1] != '/') - strHome += '/'; + pathRet = fs::path("/"); + else + pathRet = fs::path(pszHome); #ifdef MAC_OSX // Mac - strHome += "Library/Application Support/"; - filesystem::create_directory(strHome.c_str()); - return strHome + "Bitcoin"; + pathRet /= "Library" / "Application Support"; + filesystem::create_directory(pathRet); + return pathRet / "Bitcoin"; #else // Unix - return strHome + ".bitcoin"; + return pathRet / ".bitcoin"; #endif #endif } -void GetDataDir(char* pszDir) +const boost::filesystem::path &GetDataDir(bool fNetSpecific) { - // pszDir must be at least MAX_PATH length. - int nVariation; - if (pszSetDataDir[0] != 0) - { - strlcpy(pszDir, pszSetDataDir, MAX_PATH); - nVariation = 0; - } - else - { - // This can be called during exceptions by printf, so we cache the - // value so we don't have to do memory allocations after that. - static char pszCachedDir[MAX_PATH]; - if (pszCachedDir[0] == 0) - strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir)); - strlcpy(pszDir, pszCachedDir, MAX_PATH); - nVariation = 1; - } - if (fTestNet) - { - char* p = pszDir + strlen(pszDir); - if (p > pszDir && p[-1] != '/' && p[-1] != '\\') - *p++ = '/'; - strcpy(p, "testnet"); - nVariation += 2; - } - static bool pfMkdir[4]; - if (!pfMkdir[nVariation]) - { - pfMkdir[nVariation] = true; - boost::filesystem::create_directory(pszDir); + namespace fs = boost::filesystem; + + static fs::path pathCached[2]; + static CCriticalSection csPathCached; + static bool cachedPath[2] = {false, false}; + + fs::path &path = pathCached[fNetSpecific]; + + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + if (cachedPath[fNetSpecific]) + return path; + + LOCK(csPathCached); + + if (mapArgs.count("-datadir")) { + path = mapArgs["-datadir"]; + } else { + path = GetDefaultDataDir(); + if (fNetSpecific && GetBoolArg("-testnet", false)) + path /= "testnet"; } -} -string GetDataDir() -{ - char pszDir[MAX_PATH]; - GetDataDir(pszDir); - return pszDir; + fs::create_directory(path); + + cachedPath[fNetSpecific]=true; + return path; } -string GetConfigFile() +boost::filesystem::path GetConfigFile() { namespace fs = boost::filesystem; fs::path pathConfigFile(GetArg("-conf", "bitcoin.conf")); - if (!pathConfigFile.is_complete()) pathConfigFile = fs::path(GetDataDir()) / pathConfigFile; - return pathConfigFile.string(); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; + return pathConfigFile; } bool ReadConfigFile(map& mapSettingsRet, @@ -871,27 +861,13 @@ bool ReadConfigFile(map& mapSettingsRet, namespace fs = boost::filesystem; namespace pod = boost::program_options::detail; - if (mapSettingsRet.count("-datadir")) - { - if (fs::is_directory(fs::system_complete(mapSettingsRet["-datadir"]))) - { - fs::path pathDataDir(fs::system_complete(mapSettingsRet["-datadir"])); - - strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); - } - else - { - return false; - } - } - fs::ifstream streamConfig(GetConfigFile()); if (!streamConfig.good()) return true; // No bitcoin.conf file is OK set setOptions; setOptions.insert("*"); - + for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) { // Don't overwrite existing settings so command line settings override bitcoin.conf @@ -907,18 +883,18 @@ bool ReadConfigFile(map& mapSettingsRet, return true; } -string GetPidFile() +boost::filesystem::path GetPidFile() { namespace fs = boost::filesystem; fs::path pathPidFile(GetArg("-pid", "bitcoind.pid")); - if (!pathPidFile.is_complete()) pathPidFile = fs::path(GetDataDir()) / pathPidFile; - return pathPidFile.string(); + if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; + return pathPidFile; } -void CreatePidFile(string pidFile, pid_t pid) +void CreatePidFile(const boost::filesystem::path &path, pid_t pid) { - FILE* file = fopen(pidFile.c_str(), "w"); + FILE* file = fopen(path.string().c_str(), "w"); if (file) { fprintf(file, "%d\n", pid); @@ -939,8 +915,8 @@ int GetFilesize(FILE* file) void ShrinkDebugFile() { // Scroll debug.log if it's getting too big - string strFile = GetDataDir() + "/debug.log"; - FILE* file = fopen(strFile.c_str(), "r"); + boost::filesystem::path pathLog = GetDataDir() / "debug.log"; + FILE* file = fopen(pathLog.string().c_str(), "r"); if (file && GetFilesize(file) > 10 * 1000000) { // Restart the file with some of the end @@ -949,7 +925,7 @@ void ShrinkDebugFile() int nBytes = fread(pch, 1, sizeof(pch), file); fclose(file); - file = fopen(strFile.c_str(), "w"); + file = fopen(pathLog.string().c_str(), "w"); if (file) { fwrite(pch, 1, nBytes, file); diff --git a/src/util.h b/src/util.h index a04ab2c9..d205260d 100644 --- a/src/util.h +++ b/src/util.h @@ -19,6 +19,8 @@ typedef int pid_t; /* define for windows compatiblity */ #include #include +#include +#include #include #include #include @@ -111,7 +113,6 @@ extern std::map > mapMultiArgs; extern bool fDebug; extern bool fPrintToConsole; extern bool fPrintToDebugger; -extern char pszSetDataDir[MAX_PATH]; extern bool fRequestShutdown; extern bool fShutdown; extern bool fDaemon; @@ -153,16 +154,15 @@ void ParseParameters(int argc, const char*const argv[]); bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); int GetFilesize(FILE* file); -void GetDataDir(char* pszDirRet); -std::string GetConfigFile(); -std::string GetPidFile(); -void CreatePidFile(std::string pidFile, pid_t pid); +boost::filesystem::path GetDefaultDataDir(); +const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +boost::filesystem::path GetConfigFile(); +boost::filesystem::path GetPidFile(); +void CreatePidFile(const boost::filesystem::path &path, pid_t pid); bool ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef WIN32 -std::string MyGetSpecialFolderPath(int nFolder, bool fCreate); +boost::filesystem::path MyGetSpecialFolderPath(int nFolder, bool fCreate); #endif -std::string GetDefaultDataDir(); -std::string GetDataDir(); void ShrinkDebugFile(); int GetRandInt(int nMax); uint64 GetRand(uint64 nMax);