Skip to content

Commit 63fa5ae

Browse files
committed
Use AsFd in copy_file_range
And expose it on FreeBSD
1 parent 53b0332 commit 63fa5ae

File tree

3 files changed

+108
-58
lines changed

3 files changed

+108
-58
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
2424
- Added vsock support for macOS ([#2056](https://github.com/nix-rust/nix/pull/2056))
2525
- Added `SO_SETFIB` and `SO_USER_COOKIE` to `nix::sys::socket::sockopt` for FreeBSD.
2626
([#2085](https://github.com/nix-rust/nix/pull/2085))
27+
- Use I/O safety with `copy_file_range`, and expose it on FreeBSD.
28+
(#[1906](https://github.com/nix-rust/nix/pull/1906))
2729

2830
### Changed
2931

@@ -41,6 +43,15 @@ This project adheres to [Semantic Versioning](https://semver.org/).
4143
- `nix::socket` and `nix::select` are now available on Redox.
4244
([#2012](https://github.com/nix-rust/nix/pull/2012))
4345

46+
- Implemented I/O safety. Many public functions argument and return types have
47+
changed:
48+
| Original Type | New Type |
49+
| ------------- | --------------------- |
50+
| AsRawFd | AsFd |
51+
| RawFd | BorrowedFd or OwnedFd |
52+
53+
(#[1906](https://github.com/nix-rust/nix/pull/1906))
54+
4455
### Fixed
4556
- Fix: send `ETH_P_ALL` in htons format
4657
([#1925](https://github.com/nix-rust/nix/pull/1925))

src/fcntl.rs

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@ use std::ffi::OsString;
55
use std::os::raw;
66
use std::os::unix::ffi::OsStringExt;
77
use std::os::unix::io::RawFd;
8+
// For splice and copy_file_range
9+
#[cfg(any(
10+
target_os = "android",
11+
target_os = "freebsd",
12+
target_os = "linux"
13+
))]
14+
use std::{
15+
os::unix::io::{AsFd, AsRawFd},
16+
ptr,
17+
};
818

919
#[cfg(feature = "fs")]
1020
use crate::{sys::stat::Mode, NixPath, Result};
11-
#[cfg(any(target_os = "android", target_os = "linux"))]
12-
use std::ptr; // For splice and copy_file_range
1321

1422
#[cfg(any(
1523
target_os = "linux",
@@ -608,6 +616,11 @@ libc_bitflags! {
608616
feature! {
609617
#![feature = "zerocopy"]
610618

619+
#[cfg(any(target_os = "android", target_os = "linux"))]
620+
type type_of_off = libc::loff_t;
621+
#[cfg(target_os = "freebsd")]
622+
type type_of_off = libc::off_t;
623+
611624
/// Copy a range of data from one file to another
612625
///
613626
/// The `copy_file_range` system call performs an in-kernel copy between
@@ -624,32 +637,49 @@ feature! {
624637
///
625638
/// On successful completion the number of bytes actually copied will be
626639
/// returned.
627-
#[cfg(any(target_os = "android", target_os = "linux"))]
628-
pub fn copy_file_range(
629-
fd_in: RawFd,
630-
off_in: Option<&mut libc::loff_t>,
631-
fd_out: RawFd,
632-
off_out: Option<&mut libc::loff_t>,
640+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
641+
pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
642+
fd_in: Fd1,
643+
off_in: Option<&mut type_of_off>,
644+
fd_out: Fd2,
645+
off_out: Option<&mut type_of_off>,
633646
len: usize,
634647
) -> Result<usize> {
635648
let off_in = off_in
636-
.map(|offset| offset as *mut libc::loff_t)
649+
.map(|offset| offset as *mut type_of_off)
637650
.unwrap_or(ptr::null_mut());
638651
let off_out = off_out
639-
.map(|offset| offset as *mut libc::loff_t)
652+
.map(|offset| offset as *mut type_of_off)
640653
.unwrap_or(ptr::null_mut());
641654

642-
let ret = unsafe {
643-
libc::syscall(
644-
libc::SYS_copy_file_range,
645-
fd_in,
646-
off_in,
647-
fd_out,
648-
off_out,
649-
len,
650-
0,
651-
)
652-
};
655+
cfg_if::cfg_if! {
656+
if #[cfg(target_os = "freebsd")] {
657+
let ret = unsafe {
658+
libc::copy_file_range(
659+
fd_in.as_fd().as_raw_fd(),
660+
off_in,
661+
fd_out.as_fd().as_raw_fd(),
662+
off_out,
663+
len,
664+
0,
665+
)
666+
};
667+
} else {
668+
// May Linux distros still don't include copy_file_range in their
669+
// libc implementations, so we need to make a direct syscall.
670+
let ret = unsafe {
671+
libc::syscall(
672+
libc::SYS_copy_file_range,
673+
fd_in,
674+
off_in,
675+
fd_out.as_fd().as_raw_fd(),
676+
off_out,
677+
len,
678+
0,
679+
)
680+
};
681+
}
682+
}
653683
Errno::result(ret).map(|r| r as usize)
654684
}
655685

test/test_fcntl.rs

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::io::prelude::*;
2626
#[cfg(not(target_os = "redox"))]
2727
use std::os::unix::fs;
2828
#[cfg(not(target_os = "redox"))]
29-
use tempfile::{self, NamedTempFile};
29+
use tempfile::NamedTempFile;
3030

3131
#[test]
3232
#[cfg(not(target_os = "redox"))]
@@ -227,6 +227,51 @@ fn test_readlink() {
227227
);
228228
}
229229

230+
/// This test creates a temporary file containing the contents
231+
/// 'foobarbaz' and uses the `copy_file_range` call to transfer
232+
/// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
233+
/// resulting file is read and should contain the contents `bar`.
234+
/// The from_offset should be updated by the call to reflect
235+
/// the 3 bytes read (6).
236+
#[cfg(any(
237+
target_os = "linux",
238+
// Not available until FreeBSD 13.0
239+
all(target_os = "freebsd", fbsd14),
240+
target_os = "android"
241+
))]
242+
#[test]
243+
// QEMU does not support copy_file_range. Skip under qemu
244+
#[cfg_attr(qemu, ignore)]
245+
fn test_copy_file_range() {
246+
use nix::fcntl::copy_file_range;
247+
use std::os::unix::io::AsFd;
248+
249+
const CONTENTS: &[u8] = b"foobarbaz";
250+
251+
let mut tmp1 = tempfile::tempfile().unwrap();
252+
let mut tmp2 = tempfile::tempfile().unwrap();
253+
254+
tmp1.write_all(CONTENTS).unwrap();
255+
tmp1.flush().unwrap();
256+
257+
let mut from_offset: i64 = 3;
258+
copy_file_range(
259+
tmp1.as_fd(),
260+
Some(&mut from_offset),
261+
tmp2.as_fd(),
262+
None,
263+
3,
264+
)
265+
.unwrap();
266+
267+
let mut res: String = String::new();
268+
tmp2.rewind().unwrap();
269+
tmp2.read_to_string(&mut res).unwrap();
270+
271+
assert_eq!(res, String::from("bar"));
272+
assert_eq!(from_offset, 6);
273+
}
274+
230275
#[cfg(any(target_os = "linux", target_os = "android"))]
231276
mod linux_android {
232277
use libc::loff_t;
@@ -243,42 +288,6 @@ mod linux_android {
243288

244289
use crate::*;
245290

246-
/// This test creates a temporary file containing the contents
247-
/// 'foobarbaz' and uses the `copy_file_range` call to transfer
248-
/// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
249-
/// resulting file is read and should contain the contents `bar`.
250-
/// The from_offset should be updated by the call to reflect
251-
/// the 3 bytes read (6).
252-
#[test]
253-
// QEMU does not support copy_file_range. Skip under qemu
254-
#[cfg_attr(qemu, ignore)]
255-
fn test_copy_file_range() {
256-
const CONTENTS: &[u8] = b"foobarbaz";
257-
258-
let mut tmp1 = tempfile().unwrap();
259-
let mut tmp2 = tempfile().unwrap();
260-
261-
tmp1.write_all(CONTENTS).unwrap();
262-
tmp1.flush().unwrap();
263-
264-
let mut from_offset: i64 = 3;
265-
copy_file_range(
266-
tmp1.as_raw_fd(),
267-
Some(&mut from_offset),
268-
tmp2.as_raw_fd(),
269-
None,
270-
3,
271-
)
272-
.unwrap();
273-
274-
let mut res: String = String::new();
275-
tmp2.rewind().unwrap();
276-
tmp2.read_to_string(&mut res).unwrap();
277-
278-
assert_eq!(res, String::from("bar"));
279-
assert_eq!(from_offset, 6);
280-
}
281-
282291
#[test]
283292
fn test_splice() {
284293
const CONTENTS: &[u8] = b"abcdef123456";

0 commit comments

Comments
 (0)