clone_file/
linux.rs

1use std::fs::File;
2use std::io;
3use std::os::fd::AsRawFd;
4use std::path::Path;
5
6/// Clone a file using the FICLONE syscall
7///
8/// This is mainly tested on the `btrfs` filesystem.
9///
10/// For the details on the limitations of this method, see [`man 2 ioctl_ficlonerange`].
11///
12/// [`man 2 ioctl_ficlonerange`]: https://man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html
13pub fn clone_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dest: Q) -> io::Result<()> {
14    let src_file = File::open(src)?;
15    let dest_file = File::options()
16        .write(true)
17        .create(true)
18        .truncate(true)
19        .open(dest)?;
20
21    match unsafe { ioctl::ficlone(dest_file.as_raw_fd(), src_file.as_raw_fd() as u64) } {
22        Ok(_) => Ok(()),
23        Err(_) => Err(io::Error::last_os_error()),
24    }
25}
26
27/// Clone a range from a file using the FICLONERANGE syscall
28///
29/// This is mainly tested on the `btrfs` filesystem.
30///
31/// For the details on the limitations of this method, see [`man 2 ioctl_ficlonerange`].
32///
33/// One of the more common limitations is that `src_offset`, `src_length` and `dest_offset`
34/// must be multiples of the filesystem block size. Expect this function to fail if that is not the case.
35///
36/// [`man 2 ioctl_ficlonerange`]: https://man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html
37pub fn clone_file_range<P: AsRef<Path>, Q: AsRef<Path>>(
38    src: P,
39    src_offset: u64,
40    src_length: u64,
41    dest: Q,
42    dest_offset: u64,
43) -> io::Result<()> {
44    let src_file = File::open(src)?;
45    let dest_file = File::options().write(true).create(true).open(dest)?;
46
47    let args = ioctl::FileCloneRange {
48        src_fd: src_file.as_raw_fd() as i64,
49        src_offset,
50        src_length,
51        dest_offset,
52    };
53
54    match unsafe { ioctl::ficlonerange(dest_file.as_raw_fd(), &args) } {
55        Ok(_) => Ok(()),
56        Err(_) => Err(io::Error::last_os_error()),
57    }
58}
59
60mod ioctl {
61    pub struct FileCloneRange {
62        pub src_fd: i64,
63        pub src_offset: u64,
64        pub src_length: u64,
65        pub dest_offset: u64,
66    }
67
68    nix::ioctl_write_int!(ficlone, 0x94, 9);
69    nix::ioctl_write_ptr!(ficlonerange, 0x94, 13, FileCloneRange);
70}