Skip to content

Commit

Permalink
Made fs::copy return the length of the main stream
Browse files Browse the repository at this point in the history
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 rust-lang#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 rust-lang#44532
  • Loading branch information
stephaneyfx committed Sep 28, 2017
1 parent 44d5090 commit 61c0c9e
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 7 deletions.
21 changes: 18 additions & 3 deletions src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1374,14 +1374,17 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(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
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 4 additions & 4 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,16 +722,16 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
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)?;
Expand Down

0 comments on commit 61c0c9e

Please sign in to comment.