btrfs_uapi/reflink.rs
1//! # Lightweight file copy via `BTRFS_IOC_CLONE_RANGE`
2//!
3//! Reflinks a byte range from one file to another: the destination
4//! gains an extent reference to the source's data, no bytes are
5//! copied, and subsequent modifications are copy-on-write.
6//! Equivalent to the standard VFS `FICLONERANGE` ioctl (the btrfs
7//! and VFS encodings happen to share the same magic/number).
8
9use crate::raw::{btrfs_ioc_clone_range, btrfs_ioctl_clone_range_args};
10use std::os::{fd::AsRawFd, unix::io::BorrowedFd};
11
12/// Reflink a range of bytes from `src` to `dst`.
13///
14/// A `length` of zero is a sentinel meaning "from `src_offset` to
15/// end-of-source-file" — this matches the kernel's documented
16/// behaviour. The source and destination can be the same file.
17///
18/// # Errors
19///
20/// Common errors: `EINVAL` if offsets or length are not block-
21/// aligned (filesystem-specific; btrfs requires sector alignment
22/// for non-tail extents), `EXDEV` if the files live on different
23/// filesystems, `EPERM` if the destination is not writable, or
24/// `EOPNOTSUPP` if the filesystem doesn't support reflinks.
25pub fn clone_range(
26 src: BorrowedFd<'_>,
27 src_offset: u64,
28 length: u64,
29 dst: BorrowedFd<'_>,
30 dst_offset: u64,
31) -> nix::Result<()> {
32 let mut args = btrfs_ioctl_clone_range_args {
33 src_fd: i64::from(src.as_raw_fd()),
34 src_offset,
35 src_length: length,
36 dest_offset: dst_offset,
37 };
38 // SAFETY: `args` is fully initialised and lives for the duration
39 // of the ioctl. The kernel reads from it via copy_from_user; we
40 // do not read any output back.
41 unsafe { btrfs_ioc_clone_range(dst.as_raw_fd(), &raw mut args) }?;
42 Ok(())
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use std::mem::size_of;
49
50 #[test]
51 fn args_struct_size_matches_kernel() {
52 // s64 src_fd + 3 x u64 = 32 bytes — locked in by the kernel
53 // ABI; any drift here means the bindgen-generated layout is
54 // no longer compatible with the ioctl.
55 assert_eq!(size_of::<btrfs_ioctl_clone_range_args>(), 32);
56 }
57}