wallet: Fix detection of symlinks on Windows#34603
wallet: Fix detection of symlinks on Windows#34603achow101 wants to merge 4 commits intobitcoin:masterfrom
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. LLM Linter (✨ experimental)Possible typos and grammar issues:
2026-03-13 23:06:24 |
0c76d21 to
677297e
Compare
| (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) || | ||
| (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { | ||
| (IsSymlink(wallet_path) && fs::is_directory(wallet_path)) || | ||
| // Windows cross compile does not detect symlinks, so we need to explicilty check |
There was a problem hiding this comment.
When building on Windows either with MSVC or clang-cl, std::filesystem::is_symlink successfully detects file and folder symbolic links, but fails to detect junctions.
Since the in-code comment mentions "Windows cross compile", were you actually asking about the LLVM MinGW toolchain?
| if (file_attrs == INVALID_FILE_ATTRIBUTES) { | ||
| throw fs::filesystem_error("Unable to get file attributes", fs::PathToString(path), std::make_error_code(std::errc::invalid_argument)); | ||
| } | ||
| return (file_attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0; |
There was a problem hiding this comment.
Should we additionally check against a Reparse Point Tag value?
There was a problem hiding this comment.
I considered that, but didn't think any of the possible tag values were things that we actually want to support recursively scanning inside of.
There was a problem hiding this comment.
Since the IsSymlink fucntion returns true for entities other than symlinks, we should probably add a clarifying comment.
There was a problem hiding this comment.
The docstring states that it returns true for reparse points on windows.
| finally: | ||
| # Need to ensure access is restored for cleanup | ||
| os.chmod(wallet_dir('no_access'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) |
There was a problem hiding this comment.
Why is this removed? It's actually kind of annoying to have files that need to be chmod'd before deletion
There was a problem hiding this comment.
It isn't, the chmod testing is significantly changed. This is from #34418 and review comments about it should be left there.
| } | ||
| #endif | ||
|
|
||
| bool IsSymlink(const fs::path& path) |
There was a problem hiding this comment.
Might it be better to just override fs::is_symlink on Windows?
There was a problem hiding this comment.
I considered that, but we aren't already using fs::is_symlink so I don't think it would give us any benefit. Also, since IsSymlink returns true for more than just symlinks, I thought it wouldn't be appropriate to be override the stdlib function.
| if (IsSymlink(it->path())) { | ||
| LogWarning("Not recursively searching symlink/reparse point at '%s'", fs::PathToString(it->path())); | ||
| it.disable_recursion_pending(); | ||
| } |
There was a problem hiding this comment.
Is it intentional that we still check the symlink itself for a wallet?
If so, should we not do so even if IsSymlink throws?
There was a problem hiding this comment.
Is it intentional that we still check the symlink itself for a wallet?
Yes
If so, should we not do so even if IsSymlink throws?
status() and symlink_status() used below can also throw, so this is the same behavior.
test_scanning_sub_dir(): - Remove try/finally - we don't need to clean up after a failed test (done in this commit to maintain indentation). Regarding symlinks: bitcoin#31410 (comment) Kept some symlink creation which didn't disrupt Windows cross builds to make for a smaller diff and less cumbersome code. There is some hope of eventually getting better symlink support via bitcoin#34603. Co-authored-by: Ava Chow <github@achow101.com>
test_scanning_sub_dir(): - Remove try/finally - we don't need to clean up after a failed test (done in this commit to maintain indentation). Regarding symlinks: bitcoin#31410 (comment) Kept some symlink creation which didn't disrupt Windows cross builds to make for a smaller diff and less cumbersome code. There is some hope of eventually getting better symlink support via bitcoin#34603. Co-authored-by: Ava Chow <github@achow101.com>
677297e to
fda778d
Compare
GCC lacks detection of symlinks on Windows. IsSymlink implements this detection on Windows, and utilizes std::filesystem::is_symlink for other systems.
When searching the wallets directory for wallets, we want to avoid recursing symlinks. Although recursive_directory_iterator already doesn't recurse symlinks, since GCC doesn't implement symlink detection on Windows, we need to use IsSymlink to detect symlinks on Windows in order to avoid recursing them.
Since GCC lacks symlink detection, we need to use IsSymlink to detect symlinks on Windows. Additionally, symlinks to files are reported as being regular files rather symlnks, so we need to explicitly check with IsSymlink that a regular file is not actually a symlink.
fda778d to
9333d26
Compare
This PR implements symlink detection for Windows, since GCC doesn't support symlinks on Windows. This is used by the wallet's
ListDatabasesin order to detect symlinks and avoid recursing them. It's also used in wallet path validation since that is also checking whether paths are symlinks, and we should properly detect symlinks on Windows.Under the hood, the
IsSymlinkfunction usesstd::filesystem::is_symlinkfor non-Windows systems. On Windows, it checks a file's attributes to determine if it is a reparse point. Strictly speaking, this will also catch files that aren't symlinks, such as directories that are Onedrive mounts. I think it's fine to not be recursively searching such directories.This is based on #34418 for the multiwallet test and CI changes that it enables. The last commit also removes the skipping of the symlink checks so that we are testing that the symlink handling behavior on Windows matches that on Linux and MacOS.