]> git.lizzy.rs Git - rust.git/commitdiff
Use CopyFileEx for fs::copy on Windows
authorPeter Atashian <retep998@gmail.com>
Fri, 10 Jul 2015 08:54:00 +0000 (04:54 -0400)
committerPeter Atashian <retep998@gmail.com>
Fri, 10 Jul 2015 08:54:00 +0000 (04:54 -0400)
Adds a couple more tests for fs::copy

Signed-off-by: Peter Atashian <retep998@gmail.com>
src/libstd/fs.rs
src/libstd/sys/unix/fs.rs
src/libstd/sys/windows/c.rs
src/libstd/sys/windows/fs.rs

index c2d3d2fb0c8486caae414a04776a97e4aabf8ea7..58c535855de6023613029396766be2a572aaeae0 100644 (file)
@@ -21,7 +21,7 @@
 
 use fmt;
 use ffi::OsString;
-use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write};
+use io::{self, SeekFrom, Seek, Read, Write};
 use path::{Path, PathBuf};
 use sys::fs as fs_imp;
 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<()>
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
-    let from = from.as_ref();
-    let to = to.as_ref();
-    if !from.is_file() {
-        return Err(Error::new(ErrorKind::InvalidInput,
-                              "the source path is not an existing file"))
-    }
-
-    let mut reader = try!(File::open(from));
-    let mut writer = try!(File::create(to));
-    let perm = try!(reader.metadata()).permissions();
-
-    let ret = try!(io::copy(&mut reader, &mut writer));
-    try!(set_permissions(to, perm));
-    Ok(ret)
+    fs_imp::copy(from.as_ref(), to.as_ref())
 }
 
 /// Creates a new hard link on the filesystem.
@@ -1745,6 +1732,19 @@ fn copy_file_does_not_exist() {
         }
     }
 
+    #[test]
+    fn copy_src_does_not_exist() {
+        let tmpdir = tmpdir();
+        let from = Path2::new("test/nonexistent-bogus-path");
+        let to = tmpdir.join("out.txt");
+        check!(check!(File::create(&to)).write(b"hello"));
+        assert!(fs::copy(&from, &to).is_err());
+        assert!(!from.exists());
+        let mut v = Vec::new();
+        check!(check!(File::open(&to)).read_to_end(&mut v));
+        assert_eq!(v, b"hello");
+    }
+
     #[test]
     fn copy_file_ok() {
         let tmpdir = tmpdir();
@@ -1814,6 +1814,18 @@ fn copy_file_preserves_perm_bits() {
         check!(fs::set_permissions(&out, attr.permissions()));
     }
 
+    #[cfg(windows)]
+    #[test]
+    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!(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());
+    }
+
     #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
     #[test]
     fn symlinks_work() {
index 128284834ab01362944242581aa25349352eeb96..991e62b4ebbd7807e2c0bcb4719db75a73dfc4b9 100644 (file)
@@ -14,7 +14,7 @@
 
 use ffi::{CString, CStr, OsString, OsStr};
 use fmt;
-use io::{self, Error, SeekFrom};
+use io::{self, Error, ErrorKind, SeekFrom};
 use libc::{self, c_int, size_t, off_t, c_char, mode_t};
 use mem;
 use path::{Path, PathBuf};
@@ -516,3 +516,19 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
     buf.truncate(p);
     Ok(PathBuf::from(OsString::from_vec(buf)))
 }
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    use fs::{File, PathExt, set_permissions};
+    if !from.is_file() {
+        return Err(Error::new(ErrorKind::InvalidInput,
+                              "the source path is not an existing file"))
+    }
+
+    let mut reader = try!(File::open(from));
+    let mut writer = try!(File::create(to));
+    let perm = try!(reader.metadata()).permissions();
+
+    let ret = try!(io::copy(&mut reader, &mut writer));
+    try!(set_permissions(to, perm));
+    Ok(ret)
+}
index 2ddf685729bcba2d013997c660013b46d66e1815..16563be2cfb79ee1dbb9656bd5e34fe88eb2a121 100644 (file)
 
 pub const HANDLE_FLAG_INHERIT: libc::DWORD = 0x00000001;
 
+pub const PROGRESS_CONTINUE: libc::DWORD = 0;
+pub const PROGRESS_CANCEL: libc::DWORD = 1;
+pub const PROGRESS_STOP: libc::DWORD = 2;
+pub const PROGRESS_QUIET: libc::DWORD = 3;
+
 #[repr(C)]
 #[cfg(target_arch = "x86")]
 pub struct WSADATA {
@@ -249,6 +254,19 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
 pub type PSRWLOCK = *mut SRWLOCK;
 pub type ULONG = c_ulong;
 pub type ULONG_PTR = c_ulong;
+pub type LPBOOL = *mut BOOL;
+
+pub type LPPROGRESS_ROUTINE = ::option::Option<unsafe extern "system" fn(
+    TotalFileSize: libc::LARGE_INTEGER,
+    TotalBytesTransferred: libc::LARGE_INTEGER,
+    StreamSize: libc::LARGE_INTEGER,
+    StreamBytesTransferred: libc::LARGE_INTEGER,
+    dwStreamNumber: DWORD,
+    dwCallbackReason: DWORD,
+    hSourceFile: HANDLE,
+    hDestinationFile: HANDLE,
+    lpData: LPVOID,
+) -> DWORD>;
 
 #[repr(C)]
 pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
@@ -413,6 +431,12 @@ pub fn GetUserProfileDirectoryW(hToken: libc::HANDLE,
     pub fn SetHandleInformation(hObject: libc::HANDLE,
                                 dwMask: libc::DWORD,
                                 dwFlags: libc::DWORD) -> libc::BOOL;
+    pub fn CopyFileExW(lpExistingFileName: libc::LPCWSTR,
+                       lpNewFileName: libc::LPCWSTR,
+                       lpProgressRoutine: LPPROGRESS_ROUTINE,
+                       lpData: libc::LPVOID,
+                       pbCancel: LPBOOL,
+                       dwCopyFlags: libc::DWORD) -> libc::BOOL;
 }
 
 // Functions that aren't available on Windows XP, but we still use them and just
index 36fabe72aa0c1f70fb5665bf653aabee9780f246..ae6b20de63910079d53270f749ece6c32bfa446a 100644 (file)
@@ -575,3 +575,28 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
         PathBuf::from(OsString::from_wide(buf))
     })
 }
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    unsafe extern "system" fn callback(
+        _TotalFileSize: libc::LARGE_INTEGER,
+        TotalBytesTransferred: libc::LARGE_INTEGER,
+        _StreamSize: libc::LARGE_INTEGER,
+        _StreamBytesTransferred: libc::LARGE_INTEGER,
+        _dwStreamNumber: libc::DWORD,
+        _dwCallbackReason: libc::DWORD,
+        _hSourceFile: HANDLE,
+        _hDestinationFile: HANDLE,
+        lpData: libc::LPVOID,
+    ) -> libc::DWORD {
+        *(lpData as *mut i64) = TotalBytesTransferred;
+        c::PROGRESS_CONTINUE
+    }
+    let pfrom = to_utf16(from);
+    let pto = to_utf16(to);
+    let mut size = 0i64;
+    try!(cvt(unsafe {
+        c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
+                       &mut size as *mut _ as *mut _, ptr::null_mut(), 0)
+    }));
+    Ok(size as u64)
+}