Skip to content

Commit bd90aa6

Browse files
committed
Auto merge of #44895 - stephaneyfx:master, r=dtolnay
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
2 parents 4502e2a + 61c0c9e commit bd90aa6

File tree

2 files changed

+22
-7
lines changed

2 files changed

+22
-7
lines changed

src/libstd/fs.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1374,14 +1374,17 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
13741374
/// Note that if `from` and `to` both point to the same file, then the file
13751375
/// will likely get truncated by this operation.
13761376
///
1377-
/// On success, the total number of bytes copied is returned.
1377+
/// On success, the total number of bytes copied is returned and it is equal to
1378+
/// the length of the `to` file as reported by `metadata`.
13781379
///
13791380
/// # Platform-specific behavior
13801381
///
13811382
/// This function currently corresponds to the `open` function in Unix
13821383
/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`.
13831384
/// `O_CLOEXEC` is set for returned file descriptors.
1384-
/// On Windows, this function currently corresponds to `CopyFileEx`.
1385+
/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate
1386+
/// NTFS streams are copied but only the size of the main stream is returned by
1387+
/// this function.
13851388
/// Note that, this [may change in the future][changes].
13861389
///
13871390
/// [changes]: ../io/index.html#platform-specific-behavior
@@ -2589,13 +2592,25 @@ mod tests {
25892592
fn copy_file_preserves_streams() {
25902593
let tmp = tmpdir();
25912594
check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
2592-
assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6);
2595+
assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
25932596
assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
25942597
let mut v = Vec::new();
25952598
check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
25962599
assert_eq!(v, b"carrot".to_vec());
25972600
}
25982601

2602+
#[test]
2603+
fn copy_file_returns_metadata_len() {
2604+
let tmp = tmpdir();
2605+
let in_path = tmp.join("in.txt");
2606+
let out_path = tmp.join("out.txt");
2607+
check!(check!(File::create(&in_path)).write(b"lettuce"));
2608+
#[cfg(windows)]
2609+
check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
2610+
let copied_len = check!(fs::copy(&in_path, &out_path));
2611+
assert_eq!(check!(out_path.metadata()).len(), copied_len);
2612+
}
2613+
25992614
#[test]
26002615
fn symlinks_work() {
26012616
let tmpdir = tmpdir();

src/libstd/sys/windows/fs.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -722,16 +722,16 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
722722
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
723723
unsafe extern "system" fn callback(
724724
_TotalFileSize: c::LARGE_INTEGER,
725-
TotalBytesTransferred: c::LARGE_INTEGER,
725+
_TotalBytesTransferred: c::LARGE_INTEGER,
726726
_StreamSize: c::LARGE_INTEGER,
727-
_StreamBytesTransferred: c::LARGE_INTEGER,
728-
_dwStreamNumber: c::DWORD,
727+
StreamBytesTransferred: c::LARGE_INTEGER,
728+
dwStreamNumber: c::DWORD,
729729
_dwCallbackReason: c::DWORD,
730730
_hSourceFile: c::HANDLE,
731731
_hDestinationFile: c::HANDLE,
732732
lpData: c::LPVOID,
733733
) -> c::DWORD {
734-
*(lpData as *mut i64) = TotalBytesTransferred;
734+
if dwStreamNumber == 1 {*(lpData as *mut i64) = StreamBytesTransferred;}
735735
c::PROGRESS_CONTINUE
736736
}
737737
let pfrom = to_u16s(from)?;

0 commit comments

Comments
 (0)