summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/fcntl.rs73
-rw-r--r--test/test_fcntl.rs41
3 files changed, 107 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 61c0cc80..16b7992d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#1079](https://github.com/nix-rust/nix/pull/1079))
- Implemented `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, and `PartialEq` for most
types that support them. ([#1035](https://github.com/nix-rust/nix/pull/1035))
+- Added `copy_file_range` wrapper
+ ([#1069](https://github.com/nix-rust/nix/pull/1069))
### Changed
- Support for `ifaddrs` now present when building for Android.
diff --git a/src/fcntl.rs b/src/fcntl.rs
index 6da62d87..3d932a53 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -8,6 +8,8 @@ use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::ptr; // For splice and copy_file_range
+#[cfg(any(target_os = "android", target_os = "linux"))]
use sys::uio::IoVec; // For vmsplice
libc_bitflags!{
@@ -325,15 +327,70 @@ libc_bitflags! {
}
}
+/// Copy a range of data from one file to another
+///
+/// The `copy_file_range` system call performs an in-kernel copy between
+/// file descriptors `fd_in` and `fd_out` without the additional cost of
+/// transferring data from the kernel to user space and then back into the
+/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
+/// file descriptor `fd_out`, overwriting any data that exists within the
+/// requested range of the target file.
+///
+/// If the `off_in` and/or `off_out` arguments are used, the values
+/// will be mutated to reflect the new position within the file after
+/// copying. If they are not used, the relevant filedescriptors will be seeked
+/// to the new position.
+///
+/// On successful completion the number of bytes actually copied will be
+/// returned.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub fn copy_file_range(
+ fd_in: RawFd,
+ off_in: Option<&mut libc::loff_t>,
+ fd_out: RawFd,
+ off_out: Option<&mut libc::loff_t>,
+ len: usize,
+) -> Result<usize> {
+ let off_in = off_in
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+
+ let ret = unsafe {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out,
+ off_out,
+ len,
+ 0,
+ )
+ };
+ Errno::result(ret).map(|r| r as usize)
+}
+
#[cfg(any(target_os = "linux", target_os = "android"))]
-pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
- fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
- len: usize, flags: SpliceFFlags) -> Result<usize> {
- use std::ptr;
- let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
- let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
-
- let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
+pub fn splice(
+ fd_in: RawFd,
+ off_in: Option<&mut libc::loff_t>,
+ fd_out: RawFd,
+ off_out: Option<&mut libc::loff_t>,
+ len: usize,
+ flags: SpliceFFlags,
+) -> Result<usize> {
+ let off_in = off_in
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+
+ let ret = unsafe {
+ libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits())
+ };
Errno::result(ret).map(|r| r as usize)
}
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index bcc523bf..8d02f147 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -48,16 +48,55 @@ fn test_readlink() {
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use std::io::prelude::*;
+ use std::io::SeekFrom;
use std::os::unix::prelude::*;
use libc::loff_t;
- use nix::fcntl::{SpliceFFlags, FallocateFlags, fallocate, splice, tee, vmsplice};
+ use nix::fcntl::*;
use nix::sys::uio::IoVec;
use nix::unistd::{close, pipe, read, write};
use tempfile::{tempfile, NamedTempFile};
+ /// This test creates a temporary file containing the contents
+ /// 'foobarbaz' and uses the `copy_file_range` call to transfer
+ /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
+ /// resulting file is read and should contain the contents `bar`.
+ /// The from_offset should be updated by the call to reflect
+ /// the 3 bytes read (6).
+ ///
+ /// FIXME: This test is disabled for linux based builds, because Travis
+ /// Linux version is too old for `copy_file_range`.
+ #[test]
+ #[ignore]
+ fn test_copy_file_range() {
+ const CONTENTS: &[u8] = b"foobarbaz";
+
+ let mut tmp1 = tempfile().unwrap();
+ let mut tmp2 = tempfile().unwrap();
+
+ tmp1.write_all(CONTENTS).unwrap();
+ tmp1.flush().unwrap();
+
+ let mut from_offset: i64 = 3;
+ copy_file_range(
+ tmp1.as_raw_fd(),
+ Some(&mut from_offset),
+ tmp2.as_raw_fd(),
+ None,
+ 3,
+ )
+ .unwrap();
+
+ let mut res: String = String::new();
+ tmp2.seek(SeekFrom::Start(0)).unwrap();
+ tmp2.read_to_string(&mut res).unwrap();
+
+ assert_eq!(res, String::from("bar"));
+ assert_eq!(from_offset, 6);
+ }
+
#[test]
fn test_splice() {
const CONTENTS: &[u8] = b"abcdef123456";