Skip to content

Commit

Permalink
Use CopyFileEx for fs::copy on Windows
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Atashian <retep998@gmail.com>
  • Loading branch information
retep998 committed Jul 5, 2015
1 parent 4a21775 commit 7bf491d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
17 changes: 2 additions & 15 deletions src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use core::prelude::*;

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};
Expand Down Expand Up @@ -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.
Expand Down
17 changes: 17 additions & 0 deletions src/libstd/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,20 @@ 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> {
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)
}
28 changes: 28 additions & 0 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ pub const STD_ERROR_HANDLE: libc::DWORD = -12i32 as libc::DWORD;

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;

pub const COPY_FILE_ALLOW_DECRYPTED_DESTINATION: libc::DWORD = 0x00000008;

#[repr(C)]
#[cfg(target_arch = "x86")]
pub struct WSADATA {
Expand Down Expand Up @@ -249,6 +256,19 @@ pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
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 }
Expand Down Expand Up @@ -413,6 +433,14 @@ extern "system" {
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
Expand Down
28 changes: 28 additions & 0 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,31 @@ 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 {
// Alternative is to just grab TotalFileSize and return PROGRESS_QUIET
*(lpData as *mut i64) = TotalBytesTransferred;
c::PROGRESS_CONTINUE
}
let pfrom = to_utf16(from);
let pto = to_utf16(to);
let mut size = 0i64;
// Do we want to allow encrypted files to be copied to a decrypted destination?
try!(cvt(unsafe {
c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
&mut size as *mut _ as *mut _, ptr::null_mut(),
c::COPY_FILE_ALLOW_DECRYPTED_DESTINATION)
}));
Ok(size as u64)
}

0 comments on commit 7bf491d

Please sign in to comment.