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
| Name | What it does |
|---|---|
| flags | Must be 0; no flags are defined. |
| off_in = NULL | Use and advance fd_in's current file position, like read(2). |
| off_in = &pos | Read from pos without moving fd_in's position, like pread(2). |
| off_out = NULL / &pos | Same semantics as off_in but for the write side. |
| NFS 4.2 server-side copy | On NFS 4.2, the kernel sends a CLONE or COPY op to the server; data never crosses the network. |
| btrfs reflink | On btrfs, extents are shared via reflink; an instantaneous copy regardless of file size. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EINVAL | flags != 0, fd_in == fd_out with overlapping ranges, or fd is not a regular file. |
| EXDEV | Cross-filesystem copy on kernels before 5.3; fall back to read/write on those kernels. |
| EBADF | A file descriptor is invalid or opened with the wrong mode. |
| EFBIG | Write would exceed the file size limit or the filesystem's maximum file size. |
| EOPNOTSUPP | The 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
- copy_file_range(2), man7.org
- copy_file_range(2), FreeBSD
- 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.
