Auto merge of #26751 - retep998:copy-that-floppy, r=alexcrichton
Using the OS mechanism for copying files allows the OS to optimize the transfer using stuff such as [Offloaded Data Transfers (ODX)](https://msdn.microsoft.com/en-us/library/windows/desktop/hh848056%28v=vs.85%29.aspx). Also preserves a lot more information, including NTFS [File Streams](https://msdn.microsoft.com/en-us/library/windows/desktop/aa364404%28v=vs.85%29.aspx), which the manual implementation threw away. In addition, it is an atomic operation, unlike the manual implementation which has extra calls for copying over permissions. r? @alexcrichton
This commit is contained in:
commit
d0d37075a5
4 changed files with 93 additions and 16 deletions
|
@ -21,7 +21,7 @@ use core::prelude::*;
|
||||||
|
|
||||||
use fmt;
|
use fmt;
|
||||||
use ffi::OsString;
|
use ffi::OsString;
|
||||||
use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write};
|
use io::{self, SeekFrom, Seek, Read, Write};
|
||||||
use path::{Path, PathBuf};
|
use path::{Path, PathBuf};
|
||||||
use sys::fs as fs_imp;
|
use sys::fs as fs_imp;
|
||||||
use sys_common::{AsInnerMut, FromInner, AsInner};
|
use sys_common::{AsInnerMut, FromInner, AsInner};
|
||||||
|
@ -862,20 +862,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
||||||
let from = from.as_ref();
|
fs_imp::copy(from.as_ref(), to.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new hard link on the filesystem.
|
/// Creates a new hard link on the filesystem.
|
||||||
|
@ -1749,6 +1736,19 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn copy_file_ok() {
|
fn copy_file_ok() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
|
@ -1818,6 +1818,18 @@ mod tests {
|
||||||
check!(fs::set_permissions(&out, attr.permissions()));
|
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?
|
#[cfg(not(windows))] // FIXME(#10264) operation not permitted?
|
||||||
#[test]
|
#[test]
|
||||||
fn symlinks_work() {
|
fn symlinks_work() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use os::unix::prelude::*;
|
||||||
|
|
||||||
use ffi::{CString, CStr, OsString, OsStr};
|
use ffi::{CString, CStr, OsString, OsStr};
|
||||||
use fmt;
|
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 libc::{self, c_int, size_t, off_t, c_char, mode_t};
|
||||||
use mem;
|
use mem;
|
||||||
use path::{Path, PathBuf};
|
use path::{Path, PathBuf};
|
||||||
|
@ -516,3 +516,19 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
||||||
buf.truncate(p);
|
buf.truncate(p);
|
||||||
Ok(PathBuf::from(OsString::from_vec(buf)))
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,11 @@ pub const STD_ERROR_HANDLE: libc::DWORD = -12i32 as libc::DWORD;
|
||||||
|
|
||||||
pub const HANDLE_FLAG_INHERIT: libc::DWORD = 0x00000001;
|
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)]
|
#[repr(C)]
|
||||||
#[cfg(target_arch = "x86")]
|
#[cfg(target_arch = "x86")]
|
||||||
pub struct WSADATA {
|
pub struct WSADATA {
|
||||||
|
@ -249,6 +254,19 @@ pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
|
||||||
pub type PSRWLOCK = *mut SRWLOCK;
|
pub type PSRWLOCK = *mut SRWLOCK;
|
||||||
pub type ULONG = c_ulong;
|
pub type ULONG = c_ulong;
|
||||||
pub type ULONG_PTR = 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)]
|
#[repr(C)]
|
||||||
pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
|
pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
|
||||||
|
@ -413,6 +431,12 @@ extern "system" {
|
||||||
pub fn SetHandleInformation(hObject: libc::HANDLE,
|
pub fn SetHandleInformation(hObject: libc::HANDLE,
|
||||||
dwMask: libc::DWORD,
|
dwMask: libc::DWORD,
|
||||||
dwFlags: libc::DWORD) -> libc::BOOL;
|
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
|
// Functions that aren't available on Windows XP, but we still use them and just
|
||||||
|
|
|
@ -575,3 +575,28 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
||||||
PathBuf::from(OsString::from_wide(buf))
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue