From 61c0c9e5f21f231bca1c5594c2b6616e419b86fc Mon Sep 17 00:00:00 2001 From: Stephane Raux Date: Wed, 27 Sep 2017 19:43:05 -0700 Subject: [PATCH] Made `fs::copy` return the length of the main stream On Windows with the NTFS filesystem, `fs::copy` would return the sum of the lengths of all streams, which can be different from the length reported by `metadata` and thus confusing for users unaware of this NTFS peculiarity. This makes `fs::copy` return the same length `metadata` reports which is the value it used to return before PR #26751. Note that alternate streams are still copied; their length is just not included in the returned value. This change relies on the assumption that the stream with index 1 is always the main stream in the `CopyFileEx` callback. I could not find any official document confirming this but empirical testing has shown this to be true, regardless of whether the alternate stream is created before or after the main stream. Resolves #44532 --- src/libstd/fs.rs | 21 ++++++++++++++++++--- src/libstd/sys/windows/fs.rs | 8 ++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 91600b01298a4..b07733d3c803c 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -1374,14 +1374,17 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// Note that if `from` and `to` both point to the same file, then the file /// will likely get truncated by this operation. /// -/// On success, the total number of bytes copied is returned. +/// On success, the total number of bytes copied is returned and it is equal to +/// the length of the `to` file as reported by `metadata`. /// /// # Platform-specific behavior /// /// This function currently corresponds to the `open` function in Unix /// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. /// `O_CLOEXEC` is set for returned file descriptors. -/// On Windows, this function currently corresponds to `CopyFileEx`. +/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate +/// NTFS streams are copied but only the size of the main stream is returned by +/// this function. /// Note that, this [may change in the future][changes]. /// /// [changes]: ../io/index.html#platform-specific-behavior @@ -2589,13 +2592,25 @@ mod tests { fn copy_file_preserves_streams() { let tmp = tmpdir(); check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); - assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6); + assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); let mut v = Vec::new(); check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); assert_eq!(v, b"carrot".to_vec()); } + #[test] + fn copy_file_returns_metadata_len() { + let tmp = tmpdir(); + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + check!(check!(File::create(&in_path)).write(b"lettuce")); + #[cfg(windows)] + check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); + let copied_len = check!(fs::copy(&in_path, &out_path)); + assert_eq!(check!(out_path.metadata()).len(), copied_len); + } + #[test] fn symlinks_work() { let tmpdir = tmpdir(); diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index f2487c1b0bd0a..ae9535139d997 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -722,16 +722,16 @@ pub fn canonicalize(p: &Path) -> io::Result { pub fn copy(from: &Path, to: &Path) -> io::Result { unsafe extern "system" fn callback( _TotalFileSize: c::LARGE_INTEGER, - TotalBytesTransferred: c::LARGE_INTEGER, + _TotalBytesTransferred: c::LARGE_INTEGER, _StreamSize: c::LARGE_INTEGER, - _StreamBytesTransferred: c::LARGE_INTEGER, - _dwStreamNumber: c::DWORD, + StreamBytesTransferred: c::LARGE_INTEGER, + dwStreamNumber: c::DWORD, _dwCallbackReason: c::DWORD, _hSourceFile: c::HANDLE, _hDestinationFile: c::HANDLE, lpData: c::LPVOID, ) -> c::DWORD { - *(lpData as *mut i64) = TotalBytesTransferred; + if dwStreamNumber == 1 {*(lpData as *mut i64) = StreamBytesTransferred;} c::PROGRESS_CONTINUE } let pfrom = to_u16s(from)?;