Skip to content

Commit c82a42c

Browse files
committed
Change std::fs::copy to use copyfile on MacOS and iOS
1 parent c0086b9 commit c82a42c

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

src/libstd/fs.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1581,7 +1581,8 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
15811581
/// `O_CLOEXEC` is set for returned file descriptors.
15821582
/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate
15831583
/// NTFS streams are copied but only the size of the main stream is returned by
1584-
/// this function.
1584+
/// this function. On MacOS, this function corresponds to `copyfile` with
1585+
/// `COPYFILE_ALL`
15851586
/// Note that, this [may change in the future][changes].
15861587
///
15871588
/// [changes]: ../io/index.html#platform-specific-behavior

src/libstd/sys/unix/fs.rs

+86-1
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,10 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
827827
Ok(PathBuf::from(OsString::from_vec(buf)))
828828
}
829829

830-
#[cfg(not(any(target_os = "linux", target_os = "android")))]
830+
#[cfg(not(any(target_os = "linux",
831+
target_os = "android",
832+
target_os = "macos",
833+
target_os = "ios")))]
831834
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
832835
use crate::fs::File;
833836
if !from.is_file() {
@@ -937,3 +940,85 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
937940
writer.set_permissions(perm)?;
938941
Ok(written)
939942
}
943+
944+
#[cfg(any(target_os = "macos", target_os = "ios"))]
945+
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
946+
const COPYFILE_ACL: u32 = 1 << 0;
947+
const COPYFILE_STAT: u32 = 1 << 1;
948+
const COPYFILE_XATTR: u32 = 1 << 2;
949+
const COPYFILE_DATA: u32 = 1 << 3;
950+
951+
const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
952+
const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
953+
const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
954+
955+
const COPYFILE_STATE_COPIED: u32 = 8;
956+
957+
#[allow(non_camel_case_types)]
958+
type copyfile_state_t = *mut libc::c_void;
959+
#[allow(non_camel_case_types)]
960+
type copyfile_flags_t = u32;
961+
962+
extern "C" {
963+
fn copyfile(
964+
from: *const libc::c_char,
965+
to: *const libc::c_char,
966+
state: copyfile_state_t,
967+
flags: copyfile_flags_t,
968+
) -> libc::c_int;
969+
fn copyfile_state_alloc() -> copyfile_state_t;
970+
fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
971+
fn copyfile_state_get(
972+
state: copyfile_state_t,
973+
flag: u32,
974+
dst: *mut libc::c_void,
975+
) -> libc::c_int;
976+
}
977+
978+
struct FreeOnDrop(copyfile_state_t);
979+
impl Drop for FreeOnDrop {
980+
fn drop(&mut self) {
981+
// The code below ensures that `FreeOnDrop` is never a null pointer
982+
unsafe {
983+
// `copyfile_state_free` returns -1 if the `to` or `from` files
984+
// cannot be closed. However, this is not considerd this an
985+
// error.
986+
copyfile_state_free(self.0);
987+
}
988+
}
989+
}
990+
991+
if !from.is_file() {
992+
return Err(Error::new(ErrorKind::InvalidInput,
993+
"the source path is not an existing regular file"))
994+
}
995+
996+
// We ensure that `FreeOnDrop` never contains a null pointer so it is
997+
// always safe to call `copyfile_state_free`
998+
let state = unsafe {
999+
let state = copyfile_state_alloc();
1000+
if state.is_null() {
1001+
return Err(crate::io::Error::last_os_error());
1002+
}
1003+
FreeOnDrop(state)
1004+
};
1005+
1006+
cvt(unsafe {
1007+
copyfile(
1008+
cstr(from)?.as_ptr(),
1009+
cstr(to)?.as_ptr(),
1010+
state.0,
1011+
COPYFILE_ALL,
1012+
)
1013+
})?;
1014+
1015+
let mut bytes_copied: libc::off_t = 0;
1016+
cvt(unsafe {
1017+
copyfile_state_get(
1018+
state.0,
1019+
COPYFILE_STATE_COPIED,
1020+
&mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
1021+
)
1022+
})?;
1023+
Ok(bytes_copied as u64)
1024+
}

0 commit comments

Comments
 (0)