Preserve Windows directory symlinks in cp#12741
Conversation
4f7d22e to
e184213
Compare
|
GNU testsuite comparison: |
|
@lhecker wdyt ? |
|
Thanks, updated. |
| #[cfg(target_os = "wasi")] _symlinked_files: &mut HashSet<FileInformation>, | ||
| ) -> CopyResult<()> { | ||
| #[cfg(not(windows))] | ||
| let _ = source_type_hint; |
There was a problem hiding this comment.
Ok,I would refactor it.I previously had a misunderstanding about this.
02c2cb1 to
0b7f1cf
Compare
| Ok(()) | ||
| } | ||
|
|
||
| #[cfg(windows)] |
There was a problem hiding this comment.
it is not duplicating a lot of code ...
There was a problem hiding this comment.
Updated. I kept the platform-specific signatures, but moved the shared creation/error handling/recording path into helper functions to avoid duplicating that logic.
There was a problem hiding this comment.
Indeed, this PR would be a lot easier to review if it was closer to the original structure.
Edit: Ah, GitHub hadn't updated to show me your comment.
There was a problem hiding this comment.
Updated again. I kept the original symlink_file cfg block layout and only added a Windows hint argument plus the symlink_dir / symlink_file selection. Non-Windows no longer needs a dummy parameter.
825e3fe to
347ba06
Compare
lhecker
left a comment
There was a problem hiding this comment.
The diff is a lot tighter now which is nice.
| fn symlink_file( | ||
| source: &Path, | ||
| dest: &Path, | ||
| #[cfg(windows)] source_type_hint: &Path, |
There was a problem hiding this comment.
@sylvestre Do you know how cp's dereference mode interacts with copying symlinks? Because if it doesn't, then we can just pass source_metadata here. That already comes from walkdir's metadata and when options.dereference == false, then it refers to the symlink's metadata.
| #[cfg(windows)] | ||
| source, |
There was a problem hiding this comment.
Is this correct? My reading of the function was that it intentionally dereferences the symlink and then copies the symlink target (aka link), so whether source was a symlink or not shouldn't be relevant right?
There was a problem hiding this comment.
I think you are right here.
link is only the target path from read_link, and what we need on Windows is the symlink type from the source symlink itself.
So passing source_metadata may be a better way than passing source and stat it again. I can update it.
There was a problem hiding this comment.
Updated. copy_link now uses the existing source_metadata as the Windows symlink type hint.
23e721b to
1beed0e
Compare
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | cp_recursive_deep_tree[(120, 4)] |
562.5 KB | 615.6 KB | -8.62% |
| ⚡ | Simulation | ls_recursive_balanced_tree[(6, 4, 15)] |
52.3 ms | 49.6 ms | +5.5% |
| ⚡ | Simulation | ls_recursive_wide_tree[(10000, 1000)] |
34.3 ms | 33 ms | +3.99% |
| ⚡ | Simulation | single_date_now |
85.6 µs | 82.8 µs | +3.35% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing caomengxuan666:fix-windows-cp-directory-symlink-upstream (d33d0cd) with main (f406ea0)
Footnotes
-
46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
lhecker
left a comment
There was a problem hiding this comment.
PR appears correct to me now, with just one nit left.
| const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10; | ||
|
|
||
| let file_type = source_metadata.file_type(); | ||
| if file_type.is_symlink() { | ||
| source_metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 | ||
| } else { | ||
| file_type.is_dir() | ||
| } |
There was a problem hiding this comment.
I have read the implementation of is_dir() and found that it basically checks !is_symlink && is_directory where the latter checks for FILE_ATTRIBUTE_DIRECTORY. So, this can be simplified to:
| const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10; | |
| let file_type = source_metadata.file_type(); | |
| if file_type.is_symlink() { | |
| source_metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 | |
| } else { | |
| file_type.is_dir() | |
| } | |
| const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10; | |
| source_metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 |
There was a problem hiding this comment.
Updated, simplified as suggested.
1beed0e to
d33d0cd
Compare
Summary
cprecreates symlinkssymlink_diron Windows when the source symlink metadata has the directory attributeThis also addresses microsoft/coreutils#106 downstream.
Verification
cargo fmt -p uu_cp -- --checkcargo check -p uu_cphello_world_symlinkas<SYMLINKD> [hello_world]target\debug\cp.exe -a hello_world_symlink testtestis<SYMLINKD> [hello_world]dir testworks andtest\file.txtis readable