Win: Fix std::fs::rename failing on Windows Server by attempting the non-atomic rename first by Fulgen301 · Pull Request #138133 · rust-lang/rust (original) (raw)
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation13 Commits1 Checks8 Files changed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
We previously attempted the atomic rename first, and fell back to trying a non-atomic rename if it wasn't supported. However, this does not work reliably on Windows Server - NTFS partitions created in Windows Server 2016 apparently need to be upgraded, and it outright fails with ERROR_NOT_SUPPORTED on ReFS on Windows Server 2022.
This commit switches the two calls so that FileRenameInfo is tried first, and an atomic rename via FileRenameInfoEx is only attempted if the non-atomic rename fails.
This PR works similarly to #137528, but is a cleaner fix in my opinion and avoids additional syscalls to reopen the file if MoveFileEx fails.
r? @ChrisDenton
rustbot has assigned @ChrisDenton.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.
Use r? to explicitly pick a reviewer
Operating system: Windows
Status: Awaiting review from the assignee but also interested parties.
Relevant to the library team, which will review and decide on the PR/issue.
labels
Ok, I've been testing this on many Windows versions and on different filesystem. I think the better approach is to test for the following error codes:
ERROR_INVALID_PARAMETERERROR_NOT_SUPPORTEDERROR_INVALID_FUNCTION
ERROR_INVALID_FUNCTION seems unique to 1607. Windows versions before that consistently return ERROR_INVALID_PARAMETER. After that it can be ERROR_NOT_SUPPORTED or ERROR_INVALID_PARAMETER depending on the OS version and filesystem.
This commit switches the two calls so that FileRenameInfo is tried first, and an atomic rename via FileRenameInfoEx is only attempted if the non-atomic rename fails.
What are the non-atomic semantics on windows? Unix software generally expects renames to be atomic.
rustbot added S-waiting-on-author
Status: This is awaiting some action (such as code changes or more information) from the author.
and removed S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.
labels
Reminder, once the PR becomes ready for a review, use @rustbot ready.
@Fulgen301
Thanks for your contribution.
Any updates on this PR? Also please make rebase
I'll fix it up before the week is over.
In addition to ERROR_INVALID_PARAMETER for Windows versions older than Windows 10 1607, some Windows versions may also fail with ERROR_INVALID_FUNCTION or ERROR_NOT_SUPPORTED to indicate that atomic rename is not supported. Fallback to FileRenameInfo in those cases.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll do a proper review when I have some time to do some testing on various Windows versions, filesystems, etc. But a few quick observations:
| // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. |
|---|
| let Ok(new_len_without_nul_in_bytes): Result<u32, _> = ((new.count_bytes() - 1) * 2).try_into() |
| else { |
| return Err(WinError::INVALID_PARAMETER).io_result(); |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably ERROR_FILENAME_EXCED_RANGE would be better or else a custom error message. Even if it's not strictly what happened before.
| Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, |
|---|
| Err(err) => Some(Err(err)), |
| } |
| .unwrap_or_else(| |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the error codes that are handled I worry this doesn't account for reparse points that cannot be opened, not because they're aren't supported but because they cannot be interpreted by CreateFile. E.g. app execution aliases aren't actually links in the filesystem sense and can only be interpreted by CreateProcess. We should still be able to rename them though.
| }; |
|---|
| unsafe { |
| dealloc(file_rename_info.cast::<u8>(), layout); |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now fairly distant from the alloc. While nothing returns or panics before this atm, I wouldn't be surprised if some refactoring in the future missed this subtlety. So I would be happier with a drop guard or some other custom struct that is guaranteed to free memory wherever the scope ends.
@Fulgen301 any updates on answering the initial review? thanks
rdesgroppes added a commit to rdesgroppes/bazel that referenced this pull request
Might close bazelbuild#17192
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks). Two failure modes occur:
ReadSymlinkOrJunctionfails during path resolution with: "Cannot read link: DeviceIoControl: Incorrect function" This causes build analysis to abort completely.CreateJunctionfails when creating convenience symlinks with: "Cannot create junction: DeviceIoControl: Incorrect function" This produces cryptic error messages.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Proposed slution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clear "filesystem does not support junctions" warnings instead of cryptic errors, and doesn't abort the build.
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
External references:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel to work on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations
- transforms these environments from completely broken to usable
- convenience symlinks (bazel-bin, bazel-out, etc.) won't be created on these filesystems - users must navigate to actual output paths
- builds proceed with clear warnings instead of aborting with errors
Limitations: This is a best-effort workaround that trades convenience for functionality. Users lose workspace convenience symlinks but gain the ability to build. Full junction support would require filesystem- level changes (e.g., virtiofs driver improvements).
Testing: On Windows 11 VM with virtiofs-mounted folder: builds complete with expected warnings about unsupported junctions.
rdesgroppes added a commit to rdesgroppes/bazel that referenced this pull request
Fixes bazelbuild#17192
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path
resolution (e.g., when Starlark code calls .realpath): "Cannot read
link: DeviceIoControl: Incorrect function"
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience
symlinks produce cryptic error messages, though these were already
non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. This is the critical fix that enables builds to succeed. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clearer "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
References to similar solutions in OSS projects:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Rust (rust-lang/rust#43801): reduced symlink usage in build system after encountering os error 1 on VirtualBox shared folders.
- Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel builds on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations
- transforms these environments from completely broken to usable
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages
- users must navigate to actual output paths in the output_base
Limitations: This is a best-effort workaround that trades convenience for functionality. Users lose workspace convenience symlinks but gain the ability to build. Full junction support would require filesystem- level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
rdesgroppes added a commit to rdesgroppes/bazel that referenced this pull request
Might close bazelbuild#17192.
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path
resolution (e.g., when Starlark code calls .realpath): "Cannot read
link: DeviceIoControl: Incorrect function"
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience
symlinks produce cryptic error messages, though these were already
non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clearer "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Rust (rust-lang/rust#43801): reduced symlink usage in build system after encountering os error 1 on VirtualBox shared folders.
- Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel builds on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
rdesgroppes added a commit to rdesgroppes/bazel that referenced this pull request
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path
resolution (e.g., when Starlark code calls .realpath): "Cannot read
link: DeviceIoControl: Incorrect function"
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience
symlinks produce cryptic error messages, though these were already
non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clearer "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Rust (rust-lang/rust#43801): reduced symlink usage in build system after encountering os error 1 on VirtualBox shared folders.
- Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel builds on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
rdesgroppes added a commit to rdesgroppes/bazel that referenced this pull request
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path
resolution (e.g., when Starlark code calls .realpath): "Cannot read
link: DeviceIoControl: Incorrect function"
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience
symlinks produce cryptic error messages, though these were already
non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clearer "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel builds on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations.
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages.
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
copybara-service bot pushed a commit to bazelbuild/bazel that referenced this pull request
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path resolution (e.g., when Starlark code calls .realpath): "Cannot read link: DeviceIoControl: Incorrect function".
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience symlinks produce cryptic error messages, though these were already non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or FSCTL_SET_REPARSE_POINT operations.
Proposed solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clear "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Microsoft STL (microsoft/STL#2077): handles junctions and reparse point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel to work on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations.
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages.
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
Closes #27598.
PiperOrigin-RevId: 833360316 Change-Id: I3751602b2bd793c1cee75b7b66fa73c955a72517
bazel-io pushed a commit to bazel-io/bazel that referenced this pull request
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path resolution (e.g., when Starlark code calls .realpath): "Cannot read link: DeviceIoControl: Incorrect function".
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience symlinks produce cryptic error messages, though these were already non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or FSCTL_SET_REPARSE_POINT operations.
Proposed solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clear "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Microsoft STL (microsoft/STL#2077): handles junctions and reparse point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel to work on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations.
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages.
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
Closes bazelbuild#27598.
PiperOrigin-RevId: 833360316 Change-Id: I3751602b2bd793c1cee75b7b66fa73c955a72517
github-merge-queue bot pushed a commit to bazelbuild/bazel that referenced this pull request
Problem: Bazel fails completely on Windows when using filesystems that don't support junction/reparse point operations (e.g., virtiofs, VirtualBox shared folders, network drives, RAM disks).
The fatal error occurs when ReadSymlinkOrJunction fails during path
resolution (e.g., when Starlark code calls .realpath): "Cannot read
link: DeviceIoControl: Incorrect function".
This causes build analysis to abort completely.
Additionally, CreateJunction failures when creating convenience
symlinks produce cryptic error messages, though these were already
non-fatal warnings.
Both fail because DeviceIoControl returns ERROR_INVALID_FUNCTION
when the filesystem doesn't implement FSCTL_GET_REPARSE_POINT or
FSCTL_SET_REPARSE_POINT operations.
Proposed solution:
Handle ERROR_INVALID_FUNCTION gracefully by treating it as a "not
supported" condition rather than a fatal error:
- in
ReadSymlinkOrJunction(file.cc:592): returnkNotALinkinstead ofkErrorwhenERROR_INVALID_FUNCTIONoccurs. This allows path resolution to continue for non-symlink paths on unsupported filesystems. - in
CreateJunction(file.cc:461): return newkNotSupportedresult code whenERROR_INVALID_FUNCTIONoccurs. This produces clear "filesystem does not support junctions" warnings instead of cryptic "Incorrect function" messages. This improves UX but doesn't change behavior (these failures were already non-fatal).
This follows the try-first, fallback-on-error pattern (EAFP) used by other major projects when handling unsupported filesystem operations.
Prior art:
- Rust (rust-lang/rust#138133): checks
ERROR_INVALID_FUNCTION,ERROR_NOT_SUPPORTED, andERROR_INVALID_PARAMETERfor filesystem operation fallbacks instd::fs::rename. - Microsoft STL (microsoft/STL#2077): handles junctions and reparse
point errors including
ERROR_INVALID_PARAMETERwith robust fallback logic infilesystem.cpp. - Go (golang/go#20506): uses fallback strategies when symlink APIs are unavailable on different Windows versions.
- WinFsp (winfsp/winfsp#88): documents that
ERROR_INVALID_FUNCTIONindicatesSTATUS_NOT_IMPLEMENTEDfor unsupported operations. - Microsoft Learn: recommends checking
FILE_SUPPORTS_REPARSE_POINTSflag viaGetVolumeInformation, but try-catch approach is simpler and handles edge cases where detection succeeds but operations fail.
Impact:
- enables Bazel to work on virtiofs, VirtualBox shared folders, RAM disks, and other filesystems that don't support Windows junction operations.
- convenience symlinks (bazel-bin, bazel-out, etc.) still won't be created, but now with clearer error messages.
Limitations: Full junction support would require filesystem-level changes (e.g., virtiofs driver improvements).
Testing: Tested on Windows 11 VM with host directory mounted via virtiofs, with rules_pkg. Before change: build analysis aborted with "Cannot read link" fatal error. After change: builds complete successfully with clearer warnings about unsupported junctions for convenience symlinks.
Closes #27598.
PiperOrigin-RevId: 833360316 Change-Id: I3751602b2bd793c1cee75b7b66fa73c955a72517
Commit dab96fc
Co-authored-by: Rgis Desgroppes rdesgroppes@gmail.com
Labels
Operating system: Windows
Status: This is awaiting some action (such as code changes or more information) from the author.
Relevant to the library team, which will review and decide on the PR/issue.