diff --git a/doc/files.md b/doc/files.md
index b738d6055a2..3732a7d24a5 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 corruption when using exFAT on MacOS for Bitcoin Core. This appears to be due to filesystem-level issues with exFAT on MacOS. 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..812a781c5ae 100644
--- a/src/common/init.cpp
+++ b/src/common/init.cpp
@@ -6,8 +6,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
@@ -62,6 +64,35 @@ std::optional InitConfig(ArgsManager& args, SettingsAbortFn setting
fs::create_directories(net_path / "wallets");
}
+ // Warn if we are trying to put the datadir on an exFAT fs on MacOS
+ // This is an upstream issue known to cause bugs, see #28552
+#ifdef __APPLE__
+ struct PathCheck {
+ fs::path path;
+ std::string_view description;
+ };
+ std::array paths_to_check{{
+ {args.GetDataDirNet(), "data directory"},
+ {args.GetBlocksDirPath(), "blocks directory"}
+ }};
+ for (const auto& check : paths_to_check) {
+ FSType fs_type = GetFilesystemType(check.path);
+ switch(fs_type) {
+ case FSType::EXFAT:
+ InitWarning(strprintf(_("Specified %s \"%s\" is exFAT which is known to have intermittent corruption problems on MacOS. "
+ "See https://github.com/bitcoin/bitcoin/blob/master/doc/files.md#filesystem-recommendations for more information."),
+ check.description,
+ fs::PathToString(check.path)));
+ break;
+ case FSType::ERROR:
+ LogInfo("Failed to detect filesystem type of %s: %s\n", check.description, fs::PathToString(check.path));
+ break;
+ default:
+ break;
+ }
+ }
+#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..6bf417bb46b 100644
--- a/src/util/fs_helpers.cpp
+++ b/src/util/fs_helpers.cpp
@@ -41,6 +41,10 @@
#include /* For SHGetSpecialFolderPathW */
#endif // WIN32
+#ifdef __APPLE__
+#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 +313,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().