diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index c2d3d2fb0c8..58c535855de 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -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, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { - 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() { diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 128284834ab..991e62b4ebb 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -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 { buf.truncate(p); Ok(PathBuf::from(OsString::from_vec(buf))) } + +pub fn copy(from: &Path, to: &Path) -> io::Result { + 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) +} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 2ddf685729b..16563be2cfb 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -66,6 +66,11 @@ 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 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 diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 36fabe72aa0..ae6b20de639 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -575,3 +575,28 @@ pub fn canonicalize(p: &Path) -> io::Result { PathBuf::from(OsString::from_wide(buf)) }) } + +pub fn copy(from: &Path, to: &Path) -> io::Result { + 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) +}