File tree Expand file tree Collapse file tree 3 files changed +47
-0
lines changed
Expand file tree Collapse file tree 3 files changed +47
-0
lines changed Original file line number Diff line number Diff line change 88#include < bitcoin-build-config.h> // IWYU pragma: keep
99
1010#include < logging.h>
11+ #include < random.h>
1112#include < sync.h>
1213#include < util/fs.h>
1314#include < util/syserror.h>
@@ -304,6 +305,28 @@ std::optional<fs::perms> InterpretPermString(const std::string& s)
304305 }
305306}
306307
308+ bool IsFileWritable (const fs::path& file)
309+ {
310+ if (!fs::exists (file)) throw std::runtime_error (strprintf (" File %s does not exist" , fs::PathToString (file)));
311+ if (fs::is_directory (file)) throw std::runtime_error (strprintf (" Path %s is not a file" , fs::PathToString (file)));
312+ std::ofstream ofs (fs::PathToString (file), std::ios::app);
313+ return ofs.is_open ();
314+ }
315+
316+ bool IsDirWritable (const fs::path& dir_path) {
317+ // Attempt to create a tmp file in the directory
318+ if (!fs::is_directory (dir_path)) throw std::runtime_error (strprintf (" Path %s is not a directory" , fs::PathToString (dir_path)));
319+ FastRandomContext rng;
320+ const auto tmp = dir_path / fs::PathFromString (strprintf (" .tmp_%d" , rng.rand64 ()));
321+ if (auto created{fsbridge::fopen (tmp, " a" )}) {
322+ std::fclose (created);
323+ std::error_code ec;
324+ fs::remove (tmp, ec); // clean up, ignore errors
325+ return true ;
326+ }
327+ return false ;
328+ }
329+
307330#ifdef __APPLE__
308331FSType GetFilesystemType (const fs::path& path)
309332{
Original file line number Diff line number Diff line change @@ -93,6 +93,21 @@ std::string PermsToSymbolicString(fs::perms p);
9393 */
9494std::optional<fs::perms> InterpretPermString (const std::string& s);
9595
96+ /* * Check if a file is writable by opening in append mode.
97+ *
98+ * @param[in] file_path path of the file to test
99+ * @throw std::runtime_error if file_path does not exist or is a directory.
100+ */
101+ bool IsFileWritable (const fs::path& file_path);
102+
103+ /* * Check if a directory is writable by creating a temporary file on it.
104+ *
105+ * @param[in] dir_path path of the directory to test
106+ * @return true if a temporary file could be created and removed, false otherwise.
107+ * @throw std::runtime_error if dir_path is not a directory.
108+ */
109+ bool IsDirWritable (const fs::path& dir_path);
110+
96111#ifdef WIN32
97112fs::path GetSpecialFolderPath (int nFolder, bool fCreate = true );
98113#endif
Original file line number Diff line number Diff line change @@ -3764,6 +3764,15 @@ bool CWallet::MigrateToSQLite(bilingual_str& error)
37643764 return false ;
37653765 }
37663766
3767+ // Check parent directory and db file are writable
3768+ if (m_database->Format () != " mock" ) {
3769+ const auto file_path = fs::PathFromString (m_database->Filename ());
3770+ if (!IsFileWritable (file_path) || !IsDirWritable (file_path.parent_path ())) {
3771+ error = _ (" Error: Wallet db cannot be updated. Adjust directory or file permissions to proceed with migration." );
3772+ return false ;
3773+ }
3774+ }
3775+
37673776 // Get all of the records for DB type migration
37683777 std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch ();
37693778 std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor ();
You can’t perform that action at this time.
0 commit comments