From df1ba101419729aafa382d970ee605e2a6273e26 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. --- doc/files.md | 8 ++++++++ src/common/init.cpp | 43 +++++++++++++++++++++++++++++++++++++++++ src/util/fs_helpers.cpp | 19 ++++++++++++++++++ src/util/fs_helpers.h | 17 ++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/doc/files.md b/doc/files.md index b738d6055a2..cfdfae29aa6 100644 --- a/doc/files.md +++ b/doc/files.md @@ -16,6 +16,8 @@ - [Legacy subdirectories and files](#legacy-subdirectories-and-files) +- [Filesystem recommendations](#filesystem-recommendations) + - [Notes](#notes) ## Data directory location @@ -123,6 +125,12 @@ Path | Description | Repository notes `addr.dat` | Peer IP address BDB database; replaced by `peers.dat` in [0.7.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.7.0.md) | [PR #1198](https://github.com/bitcoin/bitcoin/pull/1198), [`928d3a01`](https://github.com/bitcoin/bitcoin/commit/928d3a011cc66c7f907c4d053f674ea77dc611cc) `onion_private_key` | Cached Tor onion service private key for `-listenonion` option. Was used for Tor v2 services; replaced by `onion_v3_private_key` in [0.21.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md) | [PR #19954](https://github.com/bitcoin/bitcoin/pull/19954) +## Filesystem recommendations + +When choosing a filesystem for the data directory (`datadir`) or blocks directory (`blocksdir`), some filesystems should be avoided: + +- **macOS**: The exFAT filesystem should not be used. There have been multiple reports of database and other corruption when using exFAT on macOS for Bitcoin Core. This appears to be due to filesystem-level issues with exFAT on the macOS operating system. See [Issue #31454](https://github.com/bitcoin/bitcoin/issues/31454) for more details. + ## Notes 1. The `/` (slash, U+002F) is used as the platform-independent path component separator in this document. diff --git a/src/common/init.cpp b/src/common/init.cpp index 5a704404689..913465b13ba 100644 --- a/src/common/init.cpp +++ b/src/common/init.cpp @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -61,7 +63,48 @@ std::optional InitConfig(ArgsManager& args, SettingsAbortFn setting if (!fs::exists(net_path)) { fs::create_directories(net_path / "wallets"); } +#ifdef __APPLE__ + struct PathCheck { + fs::path path; + std::string_view description; + }; + std::array paths{{ + {args.GetDataDirNet(), "data directory"}, + {args.GetBlocksDirPath(), "blocks directory"} + }}; + + std::vector exfat_paths; + std::vector error_paths; + + for (const auto& check : paths) { + FSType fs_type = GetFilesystemType(check.path); + switch(fs_type) { + case FSType::EXFAT: + exfat_paths.push_back(strprintf("%s (\"%s\")", + check.description, + fs::PathToString(check.path))); + break; + case FSType::ERROR: + error_paths.push_back(strprintf("%s (\"%s\")", + check.description, + fs::PathToString(check.path))); + break; + default: + break; + } + } + + if (!exfat_paths.empty()) { + InitWarning(strprintf(_("The following paths are on exFAT which is known to have intermittent corruption problems on MacOS: %s. " + "See https://github.com/bitcoin/bitcoin/blob/master/doc/files.md#filesystem-recommendations for more information."), + util::Join(exfat_paths, ", "))); + } + + if (!error_paths.empty()) { + LogInfo("Failed to detect filesystem type for: %s\n", util::Join(error_paths, ", ")); + } +#endif // Show an error or warn/log if there is a bitcoin.conf file in the // datadir that is being ignored. const fs::path base_config_path = base_path / BITCOIN_CONF_FILENAME; diff --git a/src/util/fs_helpers.cpp b/src/util/fs_helpers.cpp index 4d06afe1442..5f9fb712fcc 100644 --- a/src/util/fs_helpers.cpp +++ b/src/util/fs_helpers.cpp @@ -41,6 +41,11 @@ #include /* For SHGetSpecialFolderPathW */ #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 @@ -309,3 +314,17 @@ std::optional InterpretPermString(const std::string& s) return std::nullopt; } } + +#ifdef __APPLE__ +FSType GetFilesystemType(const fs::path& path) { + struct statfs fs_info; + if (statfs(path.c_str(), &fs_info) != 0) { + return FSType::ERROR; + } + + if (strcmp(fs_info.f_fstypename, "exfat") == 0) { + return FSType::EXFAT; + } + return FSType::OTHER; +} +#endif diff --git a/src/util/fs_helpers.h b/src/util/fs_helpers.h index 28dd6d979d5..7423da82a42 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().