$RodHat_
Syscall reference · I/O

copy_file_range(2)

Copy a range of bytes between two file descriptors entirely inside the kernel, with server-side offload on NFS 4.2 and reflink on btrfs.

Since
Linux 4.5check your kernel →
glibc
glibc 2.27
System
Linux
Man page
copy_file_range(2)

Synopsis

#define _GNU_SOURCE
#include <unistd.h>

ssize_t copy_file_range(int fd_in, off64_t *_Nullable off_in,
                        int fd_out, off64_t *_Nullable off_out,
                        size_t len, unsigned int flags);

Rod's take

cp reads your file into a userspace buffer and writes it back out. sendfile fixed file-to-socket. splice fixed the general case but required a pipe. copy_file_range is the one that was missing: file to file, entirely in the kernel, no pipe, no buffer. On NFS 4.2 the data doesn't cross the network at all. On btrfs it's a reflink and costs nothing regardless of file size. On anything else it copies in the page cache, which beats two userspace copies every time. Loop on the return like write(), handle EXDEV if you care about kernels before 5.3, and stop shipping programs that read files into buffers just to write them somewhere else.

Flags and arguments that matter

NameWhat it does
flagsMust be 0; no flags are defined.
off_in = NULLUse and advance fd_in's current file position, like read(2).
off_in = &posRead from pos without moving fd_in's position, like pread(2).
off_out = NULL / &posSame semantics as off_in but for the write side.
NFS 4.2 server-side copyOn NFS 4.2, the kernel sends a CLONE or COPY op to the server; data never crosses the network.
btrfs reflinkOn btrfs, extents are shared via reflink; an instantaneous copy regardless of file size.

Errors you will actually hit

errnoMeaning
EINVALflags != 0, fd_in == fd_out with overlapping ranges, or fd is not a regular file.
EXDEVCross-filesystem copy on kernels before 5.3; fall back to read/write on those kernels.
EBADFA file descriptor is invalid or opened with the wrong mode.
EFBIGWrite would exceed the file size limit or the filesystem's maximum file size.
EOPNOTSUPPThe filesystem does not support this operation.

Example

/* copy entire file, kernel handles the fast path */
struct stat st;
if (fstat(fd_in, &st) < 0)
    err(1, "fstat");

size_t left = st.st_size;
while (left > 0) {
    ssize_t n = copy_file_range(fd_in, NULL, fd_out, NULL, left, 0);
    if (n < 0) {
        if (errno == EXDEV) goto fallback;  /* kernel < 5.3 */
        err(1, "copy_file_range");
    }
    if (n == 0) break;          /* EOF or sparse hole */
    left -= n;
}

Trimmed from the full walkthrough in copy_file_range: file-to-file copies that never leave the kernel.

BSD equivalent

FreeBSD: copy_file_range(2) (FreeBSD 13.0), identical interface; uses ZFS reflink when both fds are on the same dataset.

The long version, from the console

Sources

  1. copy_file_range(2), man7.org
  2. copy_file_range(2), FreeBSD
  3. RFC 7862: NFS Version 4 Minor Version 2 Protocol

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.