From 115b6b6291820384d6fbb6a49972af0b89147d0a Mon Sep 17 00:00:00 2001 From: yushuoqi Date: Fri, 5 Jun 2026 11:18:13 +0800 Subject: [PATCH] fast_io.rs: replace the unsafe libc::copy_file_range call by rustix Replace the unsafe `libc::copy_file_range` call with the safe `rustix::fs::copy_file_range`. Closes: #442 --- Cargo.lock | 1 + Cargo.toml | 2 + src/sed/fast_io.rs | 101 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00b1c07..64708f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,6 +1215,7 @@ dependencies = [ "rand", "regex", "rlimit", + "rustix", "sha2", "sysinfo", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 7959db7..fb2daf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ tempfile = "3.10.1" terminal_size = "0.4.2" textwrap = { version = "0.16.1", features = ["terminal_size"] } uucore = { version = "0.9.0", features = ["libc"] } +rustix = "1.1.4" xattr = "1.3.1" @@ -71,6 +72,7 @@ tempfile = { workspace = true } terminal_size = { workspace = true } textwrap = { workspace = true } uucore = { workspace = true } +rustix = { workspace = true } [dev-dependencies] assert_fs = { workspace = true } diff --git a/src/sed/fast_io.rs b/src/sed/fast_io.rs index e6017bd..2a97bea 100644 --- a/src/sed/fast_io.rs +++ b/src/sed/fast_io.rs @@ -29,6 +29,11 @@ use std::marker::PhantomData; #[cfg(unix)] use std::os::fd::RawFd; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use rustix::fd::BorrowedFd; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use rustix::fs::copy_file_range as rustix_copy_file_range; + #[cfg(unix)] use std::os::unix::io::AsRawFd; @@ -927,30 +932,32 @@ fn reliable_copy_file_range( ) -> std::io::Result { let mut pending = len; while pending > 0 { - let ret = unsafe { - libc::copy_file_range( - in_fd, - &raw mut in_off, - out_fd, - std::ptr::null_mut(), // Use and update output offset - pending, - 0, - ) - }; - if ret < 0 { - let err = io::Error::last_os_error(); - return match err.raw_os_error() { - Some(libc::ENOSYS) | Some(libc::EOPNOTSUPP) | Some(libc::EXDEV) => { - // Fallback to write(2). - reliable_write(out_fd, in_ptr, pending) - } - _ => Err(err), - }; - } else if ret == 0 { - // EOF reached - break; + let mut in_off_u64 = in_off as u64; + let result: std::io::Result = rustix_copy_file_range( + unsafe { BorrowedFd::borrow_raw(in_fd) }, + Some(&mut in_off_u64), + unsafe { BorrowedFd::borrow_raw(out_fd) }, + None, + pending, + ) + .map_err(std::io::Error::from); + + match result { + Ok(0) => break, + Ok(ret) => { + pending -= ret; + in_off = in_off_u64 as libc::off_t; + } + Err(err) => { + return match err.raw_os_error() { + Some(libc::ENOSYS) | Some(libc::EOPNOTSUPP) | Some(libc::EXDEV) => { + // Fallback to write(2). + reliable_write(out_fd, in_ptr, pending) + } + _ => Err(err), + }; + } } - pending -= ret as usize; } Ok(len) } @@ -1828,6 +1835,54 @@ mod tests { assert_eq!(&buf, b"abcdefgh"); } + #[test] + #[cfg(all(target_os = "linux", target_env = "gnu"))] + fn test_reliable_copy_file_range_rustix_path() { + let mut infile = tempfile().unwrap(); + let mut outfile = tempfile().unwrap(); + + let data = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + infile.write_all(data).unwrap(); + infile.rewind().unwrap(); + + let in_fd = infile.as_raw_fd(); + let out_fd = outfile.as_raw_fd(); + let in_ptr = data.as_ptr(); + + // Copy a subrange starting at offset 10. + let copied = reliable_copy_file_range(in_ptr, in_fd, 10, out_fd, 16).unwrap(); + + assert_eq!(copied, 16); + + outfile.rewind().unwrap(); + let mut buf = Vec::new(); + outfile.read_to_end(&mut buf).unwrap(); + assert_eq!(&buf, b"ABCDEFGHIJKLMNOP"); + + // Now verify that copying into a file with existing data appends correctly. + let mut infile2 = tempfile().unwrap(); + let mut outfile2 = tempfile().unwrap(); + let input_data = b"abcdefghijklmnopqrstuvwxyz"; + infile2.write_all(input_data).unwrap(); + infile2.rewind().unwrap(); + + outfile2.write_all(b"PRE:").unwrap(); + outfile2.flush().unwrap(); + + let in_fd2 = infile2.as_raw_fd(); + let out_fd2 = outfile2.as_raw_fd(); + let in_ptr2 = input_data.as_ptr(); + + let copied2 = reliable_copy_file_range(in_ptr2, in_fd2, 5, out_fd2, 10).unwrap(); + + assert_eq!(copied2, 10); + + outfile2.rewind().unwrap(); + let mut buf2 = Vec::new(); + outfile2.read_to_end(&mut buf2).unwrap(); + assert_eq!(&buf2, b"PRE:fghijklmno"); + } + /////////////////////////////// // Unit tests for write_chunk() ///////////////////////////////