From db3228042b2278faf7713a6ed40cdbf8e62b8fd4 Mon Sep 17 00:00:00 2001 From: willcl-ark Date: Thu, 5 Dec 2024 22:06:13 +0000 Subject: [PATCH] util: detect and warn when using exFAT on macOS exFAT is known to cause corruption on macOS. See #28552. Therefore we should warn when using this fs format for either the blocks or data directories on macOS. Co-authored-by: l0rinc --- doc/files.md | 7 +++++++ src/init.cpp | 20 ++++++++++++++++++++ src/util/fs_helpers.cpp | 17 +++++++++++++++++ src/util/fs_helpers.h | 17 +++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/doc/files.md b/doc/files.md index 1e5abdbc4ef..5ef1e49275d 100644 --- a/doc/files.md +++ b/doc/files.md @@ -18,6 +18,8 @@ - [Installed Files](#installed-files) +- [Filesystem recommendations](#filesystem-recommendations) + ## Data directory location The data directory is the default location where the Bitcoin Core files are stored. @@ -160,3 +162,8 @@ This table describes the files installed by Bitcoin Core across different platfo - *Italicized* files are only installed in source builds if relevant CMake options are enabled. They are not included in binary releases. - README and bitcoin.conf files are included in binary releases but not installed in source builds. - On Windows, binaries have a `.exe` suffix (e.g., `bitcoin-cli.exe`). + +## Filesystem recommendations + +When choosing a filesystem for the data directory (`datadir`) or blocks directory (`blocksdir`) on **macOS**,the `exFAT` filesystem should be avoided. +There have been multiple reports of database corruption and data loss when using this filesystem with Bitcoin Core, see [Issue #31454](https://github.com/bitcoin/bitcoin/issues/31454) for more details. diff --git a/src/init.cpp b/src/init.cpp index ac401bb73e9..2c2e3b77d50 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1866,6 +1866,26 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } } +#ifdef __APPLE__ + auto check_and_warn_fs{[&](const fs::path& path, std::string_view desc) { + const auto path_desc{strprintf("%s (\"%s\")", desc, fs::PathToString(path))}; + switch (GetFilesystemType(path)) { + case FSType::EXFAT: + InitWarning(strprintf(_("The %s path uses exFAT, which is known to have intermittent corruption problems on macOS. " + "Move this directory to a different filesystem to avoid data loss."), path_desc)); + break; + case FSType::ERROR: + LogInfo("Failed to detect filesystem type for %s", path_desc); + break; + case FSType::OTHER: + break; + } + }}; + + check_and_warn_fs(args.GetDataDirNet(), "data directory"); + check_and_warn_fs(args.GetBlocksDirPath(), "blocks directory"); +#endif + #if HAVE_SYSTEM const std::string block_notify = args.GetArg("-blocknotify", ""); if (!block_notify.empty()) { diff --git a/src/util/fs_helpers.cpp b/src/util/fs_helpers.cpp index be7f1ee5a27..b5dc1cb3320 100644 --- a/src/util/fs_helpers.cpp +++ b/src/util/fs_helpers.cpp @@ -30,6 +30,11 @@ #include #endif // WIN32 +#ifdef __APPLE__ +#include +#include +#endif + /** Mutex to protect dir_locks. */ static GlobalMutex cs_dir_locks; /** A map that contains all the currently held directory locks. After @@ -298,3 +303,15 @@ std::optional InterpretPermString(const std::string& s) return std::nullopt; } } + +#ifdef __APPLE__ +FSType GetFilesystemType(const fs::path& path) +{ + if (struct statfs fs_info; statfs(path.c_str(), &fs_info)) { + return FSType::ERROR; + } else if (std::string_view{fs_info.f_fstypename} == "exfat") { + return FSType::EXFAT; + } + return FSType::OTHER; +} +#endif diff --git a/src/util/fs_helpers.h b/src/util/fs_helpers.h index 28dd6d979d5..4d11f828092 100644 --- a/src/util/fs_helpers.h +++ b/src/util/fs_helpers.h @@ -14,6 +14,23 @@ #include #include +#ifdef __APPLE__ +enum class FSType { + EXFAT, + OTHER, + ERROR +}; + +/** + * Detect filesystem type for a given path. + * Currently identifies exFAT filesystems which cause issues on macOS. + * + * @param[in] path The directory path to check + * @return FSType enum indicating the filesystem type + */ +FSType GetFilesystemType(const fs::path& path); +#endif + /** * Ensure file contents are fully committed to disk, using a platform-specific * feature analogous to fsync().