Skip to content

Commit 1d20269

Browse files
committedJul 10, 2015
Use CopyFileEx for fs::copy on Windows
Adds a couple more tests for fs::copy Signed-off-by: Peter Atashian <retep998@gmail.com>
1 parent e6a9be1 commit 1d20269

File tree

4 files changed

+93
-16
lines changed

4 files changed

+93
-16
lines changed
 

‎src/libstd/fs.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use core::prelude::*;
2121

2222
use fmt;
2323
use ffi::OsString;
24-
use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write};
24+
use io::{self, SeekFrom, Seek, Read, Write};
2525
use path::{Path, PathBuf};
2626
use sys::fs as fs_imp;
2727
use sys_common::{AsInnerMut, FromInner, AsInner};
@@ -858,20 +858,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
858858
/// ```
859859
#[stable(feature = "rust1", since = "1.0.0")]
860860
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
861-
let from = from.as_ref();
862-
let to = to.as_ref();
863-
if !from.is_file() {
864-
return Err(Error::new(ErrorKind::InvalidInput,
865-
"the source path is not an existing file"))
866-
}
867-
868-
let mut reader = try!(File::open(from));
869-
let mut writer = try!(File::create(to));
870-
let perm = try!(reader.metadata()).permissions();
871-
872-
let ret = try!(io::copy(&mut reader, &mut writer));
873-
try!(set_permissions(to, perm));
874-
Ok(ret)
861+
fs_imp::copy(from.as_ref(), to.as_ref())
875862
}
876863

877864
/// Creates a new hard link on the filesystem.
@@ -1745,6 +1732,19 @@ mod tests {
17451732
}
17461733
}
17471734

1735+
#[test]
1736+
fn copy_src_does_not_exist() {
1737+
let tmpdir = tmpdir();
1738+
let from = Path2::new("test/nonexistent-bogus-path");
1739+
let to = tmpdir.join("out.txt");
1740+
check!(check!(File::create(&to)).write(b"hello"));
1741+
assert!(fs::copy(&from, &to).is_err());
1742+
assert!(!from.exists());
1743+
let mut v = Vec::new();
1744+
check!(check!(File::open(&to)).read_to_end(&mut v));
1745+
assert_eq!(v, b"hello");
1746+
}
1747+
17481748
#[test]
17491749
fn copy_file_ok() {
17501750
let tmpdir = tmpdir();
@@ -1814,6 +1814,18 @@ mod tests {
18141814
check!(fs::set_permissions(&out, attr.permissions()));
18151815
}
18161816

1817+
#[cfg(windows)]
1818+
#[test]
1819+
fn copy_file_preserves_streams() {
1820+
let tmp = tmpdir();
1821+
check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
1822+
assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6);
1823+
assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
1824+
let mut v = Vec::new();
1825+
check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
1826+
assert_eq!(v, b"carrot".to_vec());
1827+
}
1828+
18171829
#[cfg(not(windows))] // FIXME(#10264) operation not permitted?
18181830
#[test]
18191831
fn symlinks_work() {

‎src/libstd/sys/unix/fs.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use os::unix::prelude::*;
1414

1515
use ffi::{CString, CStr, OsString, OsStr};
1616
use fmt;
17-
use io::{self, Error, SeekFrom};
17+
use io::{self, Error, ErrorKind, SeekFrom};
1818
use libc::{self, c_int, size_t, off_t, c_char, mode_t};
1919
use mem;
2020
use path::{Path, PathBuf};
@@ -516,3 +516,19 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
516516
buf.truncate(p);
517517
Ok(PathBuf::from(OsString::from_vec(buf)))
518518
}
519+
520+
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
521+
use fs::{File, PathExt, set_permissions};
522+
if !from.is_file() {
523+
return Err(Error::new(ErrorKind::InvalidInput,
524+
"the source path is not an existing file"))
525+
}
526+
527+
let mut reader = try!(File::open(from));
528+
let mut writer = try!(File::create(to));
529+
let perm = try!(reader.metadata()).permissions();
530+
531+
let ret = try!(io::copy(&mut reader, &mut writer));
532+
try!(set_permissions(to, perm));
533+
Ok(ret)
534+
}

‎src/libstd/sys/windows/c.rs

+24
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ pub const STD_ERROR_HANDLE: libc::DWORD = -12i32 as libc::DWORD;
6666

6767
pub const HANDLE_FLAG_INHERIT: libc::DWORD = 0x00000001;
6868

69+
pub const PROGRESS_CONTINUE: libc::DWORD = 0;
70+
pub const PROGRESS_CANCEL: libc::DWORD = 1;
71+
pub const PROGRESS_STOP: libc::DWORD = 2;
72+
pub const PROGRESS_QUIET: libc::DWORD = 3;
73+
6974
#[repr(C)]
7075
#[cfg(target_arch = "x86")]
7176
pub struct WSADATA {
@@ -249,6 +254,19 @@ pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
249254
pub type PSRWLOCK = *mut SRWLOCK;
250255
pub type ULONG = c_ulong;
251256
pub type ULONG_PTR = c_ulong;
257+
pub type LPBOOL = *mut BOOL;
258+
259+
pub type LPPROGRESS_ROUTINE = ::option::Option<unsafe extern "system" fn(
260+
TotalFileSize: libc::LARGE_INTEGER,
261+
TotalBytesTransferred: libc::LARGE_INTEGER,
262+
StreamSize: libc::LARGE_INTEGER,
263+
StreamBytesTransferred: libc::LARGE_INTEGER,
264+
dwStreamNumber: DWORD,
265+
dwCallbackReason: DWORD,
266+
hSourceFile: HANDLE,
267+
hDestinationFile: HANDLE,
268+
lpData: LPVOID,
269+
) -> DWORD>;
252270

253271
#[repr(C)]
254272
pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
@@ -413,6 +431,12 @@ extern "system" {
413431
pub fn SetHandleInformation(hObject: libc::HANDLE,
414432
dwMask: libc::DWORD,
415433
dwFlags: libc::DWORD) -> libc::BOOL;
434+
pub fn CopyFileExW(lpExistingFileName: libc::LPCWSTR,
435+
lpNewFileName: libc::LPCWSTR,
436+
lpProgressRoutine: LPPROGRESS_ROUTINE,
437+
lpData: libc::LPVOID,
438+
pbCancel: LPBOOL,
439+
dwCopyFlags: libc::DWORD) -> libc::BOOL;
416440
}
417441

418442
// Functions that aren't available on Windows XP, but we still use them and just

‎src/libstd/sys/windows/fs.rs

+25
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,28 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
575575
PathBuf::from(OsString::from_wide(buf))
576576
})
577577
}
578+
579+
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
580+
unsafe extern "system" fn callback(
581+
_TotalFileSize: libc::LARGE_INTEGER,
582+
TotalBytesTransferred: libc::LARGE_INTEGER,
583+
_StreamSize: libc::LARGE_INTEGER,
584+
_StreamBytesTransferred: libc::LARGE_INTEGER,
585+
_dwStreamNumber: libc::DWORD,
586+
_dwCallbackReason: libc::DWORD,
587+
_hSourceFile: HANDLE,
588+
_hDestinationFile: HANDLE,
589+
lpData: libc::LPVOID,
590+
) -> libc::DWORD {
591+
*(lpData as *mut i64) = TotalBytesTransferred;
592+
c::PROGRESS_CONTINUE
593+
}
594+
let pfrom = to_utf16(from);
595+
let pto = to_utf16(to);
596+
let mut size = 0i64;
597+
try!(cvt(unsafe {
598+
c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
599+
&mut size as *mut _ as *mut _, ptr::null_mut(), 0)
600+
}));
601+
Ok(size as u64)
602+
}

0 commit comments

Comments
 (0)
Please sign in to comment.