Skip to content

Commit a0fcafd

Browse files
host_env: Use Rustix for copy_file_range (RustPython#8099)
Rustix bypasses `libc` to make direct syscalls on Linux. Rustix is also semantically cleaner than `libc` itself. For `copy_file_range`, the offsets can't be negative but `libc`'s `off_t` type is an `i64` because it's used elsewhere, including for functions that CAN take a negative offset. Rustix is cleaner because the offsets are `u64`.
1 parent 370fc76 commit a0fcafd

3 files changed

Lines changed: 28 additions & 27 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ radium = "1.1.1"
284284
rand = "0.10"
285285
rapidhash = "4.4.1"
286286
result-like = "0.5.0"
287-
rustix = { version = "1.1", features = ["event", "param", "system"] }
287+
rustix = { version = "1.1", features = ["event", "fs", "param", "system"] }
288288
rustls = { version = "0.23.39", default-features = false }
289289
rustls-graviola = "0.3"
290290
rustls-native-certs = "0.8"

crates/host_env/src/os.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -256,30 +256,14 @@ pub fn system(command: &CStr) -> libc::c_int {
256256
#[cfg(target_os = "linux")]
257257
pub fn copy_file_range(
258258
src: crt_fd::Borrowed<'_>,
259-
offset_src: Option<&mut crt_fd::Offset>,
259+
offset_src: Option<&mut u64>,
260260
dst: crt_fd::Borrowed<'_>,
261-
offset_dst: Option<&mut crt_fd::Offset>,
261+
offset_dst: Option<&mut u64>,
262262
count: usize,
263-
) -> io::Result<usize> {
264-
#[allow(clippy::unnecessary_option_map_or_else)]
265-
let p_offset_src = offset_src.map_or_else(core::ptr::null_mut, |x| x as *mut _);
266-
#[allow(clippy::unnecessary_option_map_or_else)]
267-
let p_offset_dst = offset_dst.map_or_else(core::ptr::null_mut, |x| x as *mut _);
268-
269-
// Why not use `libc::copy_file_range`: On musl, the libc wrapper may be missing.
270-
let ret = unsafe {
271-
libc::syscall(
272-
libc::SYS_copy_file_range,
273-
src.as_raw(),
274-
p_offset_src,
275-
dst.as_raw(),
276-
p_offset_dst,
277-
count,
278-
0u32,
279-
)
280-
};
281-
282-
usize::try_from(ret).map_err(|_| io::Error::last_os_error())
263+
) -> rustix::io::Result<usize> {
264+
// `copy_file_range` isn't wrapped in every libc (i.e. musl).
265+
// However, Rustix is a safe wrapper around the syscall that bypasses libc.
266+
rustix::fs::copy_file_range(src, offset_src, dst, offset_dst, count)
283267
}
284268

285269
pub fn rename(
@@ -369,6 +353,13 @@ impl ErrorExt for io::Error {
369353
}
370354
}
371355

356+
#[cfg(all(not(windows), not(target_arch = "wasm32")))]
357+
impl ErrorExt for rustix::io::Errno {
358+
fn posix_errno(&self) -> i32 {
359+
self.raw_os_error()
360+
}
361+
}
362+
372363
/// Get the last error from C runtime library functions (like _dup, _dup2, _fstat, etc.)
373364
/// CRT functions set errno, not GetLastError(), so we need to read errno directly.
374365
#[cfg(windows)]

crates/vm/src/stdlib/os.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,20 +1709,30 @@ pub(super) mod _os {
17091709

17101710
#[cfg(target_os = "linux")]
17111711
#[pyfunction]
1712-
fn copy_file_range(mut args: CopyFileRangeArgs<'_>, vm: &VirtualMachine) -> PyResult<usize> {
1712+
fn copy_file_range(args: CopyFileRangeArgs<'_>, vm: &VirtualMachine) -> PyResult<usize> {
17131713
let count: usize = args
17141714
.count
17151715
.try_into()
17161716
.map_err(|_| vm.new_value_error("count should >= 0"))?;
1717+
let mut offset_src = args
1718+
.offset_src
1719+
.map(TryInto::try_into)
1720+
.transpose()
1721+
.map_err(|_| vm.new_value_error("offset_src should be >= 0"))?;
1722+
let mut offset_dst = args
1723+
.offset_dst
1724+
.map(TryInto::try_into)
1725+
.transpose()
1726+
.map_err(|_| vm.new_value_error("offset_dst should be >= 0"))?;
17171727

17181728
crate::host_env::os::copy_file_range(
17191729
args.src,
1720-
args.offset_src.as_mut(),
1730+
offset_src.as_mut(),
17211731
args.dst,
1722-
args.offset_dst.as_mut(),
1732+
offset_dst.as_mut(),
17231733
count,
17241734
)
1725-
.map_err(|_| vm.new_last_errno_error())
1735+
.map_err(|e| vm.new_errno_error(e.raw_os_error(), e.to_string()).upcast())
17261736
}
17271737

17281738
#[pyfunction]

0 commit comments

Comments
 (0)