diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index b1550e946..d8f3577e0 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; +use std::convert::TryInto; use std::ffi; use std::fs::File; use std::fs::OpenOptions; @@ -1195,6 +1197,50 @@ mod _os { } } + #[derive(FromArgs)] + struct CopyFileRangeArgs { + #[pyarg(positional)] + src: i32, + #[pyarg(positional)] + dst: i32, + #[pyarg(positional)] + count: i64, + #[pyarg(any, default)] + offset_src: Option, + #[pyarg(any, default)] + offset_dst: Option, + } + + #[cfg(unix)] + #[pyfunction] + fn copy_file_range(args: CopyFileRangeArgs, vm: &VirtualMachine) -> PyResult { + let p_offset_src = args.offset_src.as_ref().map_or_else(std::ptr::null, |x| x); + let p_offset_dst = args.offset_dst.as_ref().map_or_else(std::ptr::null, |x| x); + let count: usize = args + .count + .try_into() + .map_err(|_| vm.new_value_error("count should > 0".to_string()))?; + + // The flags argument is provided to allow + // for future extensions and currently must be to 0. + let flags = 0u32; + + // Safety: p_offset_src and p_offset_dst is a unique pointer for offset_src and offset_dst respectively, + // and will only be freed after this function ends. + let ret = unsafe { + libc::copy_file_range( + args.src, + p_offset_src as *mut i64, + args.dst, + p_offset_dst as *mut i64, + count, + flags, + ) + }; + + usize::try_from(ret).map_err(|_| vm.new_os_error("Fail to copy_file_range".to_string())) + } + #[pyfunction] fn strerror(e: i32) -> String { unsafe { ffi::CStr::from_ptr(libc::strerror(e)) }