possum/sys/clonefile/
sys.rs1#![allow(unused_imports)]
2use std::ffi::CString;
3#[cfg(target_os = "linux")]
4use std::fs::File;
5use std::io;
6use std::io::{Error, ErrorKind};
7#[cfg(unix)]
8use std::os::unix::ffi::OsStrExt;
9use std::path::Path;
10
11use libc::{ENOTSUP, EOPNOTSUPP};
12
13use super::*;
14use crate::cpathbuf::CPathBuf;
15use crate::Error::UnsupportedFilesystem;
16use crate::PubResult;
17cfg_if! {
18 if #[cfg(windows)] {
19 }
20}
21
22pub trait CloneFileError {
23 fn is_unsupported(&self) -> bool;
24}
25
26impl CloneFileError for NativeIoError {
27 #[cfg(unix)]
28 fn is_unsupported(&self) -> bool {
29 self.raw_os_error()
30 .map(|errno| matches!(errno, EOPNOTSUPP | libc::EXDEV))
31 .unwrap_or_default()
32 }
33 #[cfg(windows)]
34 fn is_unsupported(&self) -> bool {
35 false
38 }
39}
40
41#[allow(dead_code)]
42#[cfg(unix)]
43fn last_errno() -> NativeIoError {
45 io::Error::last_os_error()
46 }
54
55pub fn clonefile(src_path: &Path, dst_path: &Path) -> Result<(), std::io::Error> {
58 cfg_if! {
59 if #[cfg(target_os = "macos")] {
60 let src_buf = CString::new(src_path.as_os_str().as_bytes()).unwrap();
61 let dst_buf = CString::new(dst_path.as_os_str().as_bytes()).unwrap();
62 let src = src_buf.as_ptr();
63 let dst = dst_buf.as_ptr();
64 let val = unsafe { libc::clonefile(src, dst, 0) };
65 if val != 0 {
66 return Err(last_errno());
67 }
68 } else {
69 let src_file = File::open(src_path)?;
70 fclonefile_noflags(&src_file, dst_path)?;
71 }
72 }
73 Ok(())
74}
75
76#[allow(unused_variables)]
78pub fn fclonefile_noflags(src_file: &File, dst_path: &Path) -> Result<(), NativeIoError> {
79 cfg_if! {
80 if #[cfg(windows)] {
81 use std::ffi::c_void;
82 let dst_file = File::create(dst_path)?;
83 let dst_handle = std_handle_to_windows(dst_file.as_raw_handle());
84 let src_metadata = src_file.metadata()?;
85 let byte_count = src_metadata.len() as i64;
86 let data = DUPLICATE_EXTENTS_DATA {
87 FileHandle: HANDLE(src_file.as_raw_handle() as isize),
88 SourceFileOffset: 0,
89 TargetFileOffset: 0,
90 ByteCount: byte_count,
91 };
92 let data_ptr = &data as *const _ as *const c_void;
93 unsafe {
94 DeviceIoControl
95 (
96 dst_handle,
97 FSCTL_DUPLICATE_EXTENTS_TO_FILE,
98 Some(data_ptr),
99 std::mem::size_of_val(&data) as u32,
100 None,
101 0,
102 None,
103 None,
104 )}?;
105 Ok(())
106 } else if #[cfg(target_os = "linux")] {
107 let dst_file = File::create(dst_path)?;
109 let src_fd = src_file.as_raw_fd();
110 let dst_fd = dst_file.as_raw_fd();
111 #[allow(clippy::useless_conversion)]
113 let request = libc::FICLONE.try_into().unwrap();
114 let rv = unsafe { libc::ioctl(dst_fd, request, src_fd) };
115 if rv == -1 {
116 return Err(last_errno());
117 }
118 Ok(())
119 } else if #[cfg(target_os = "freebsd")] {
120 unimplemented!()
134 } else {
135 let dst_buf = CPathBuf::try_from(dst_path).unwrap();
137 let dst = dst_buf.as_ptr();
138 let val = unsafe { libc::fclonefileat(src_file.as_raw_fd(), -1, dst, 0) };
139 if val != 0 {
140 return Err(last_errno());
141 }
142 Ok(())
143 }
144 }
145}