COPY_FILE_RANGE - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
copy_file_range-将数据范围从一个文件复制到另一个文件
语法
#define _GNU_SOURCE #include <unistd.h> ssize_t copy_file_range(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
说明
copy_file_range()系统调用在两个文件描述符之间执行内核内复制,而没有将数据从内核传输到用户空间然后再传输回内核的额外费用。它将最多len字节的数据从源文件描述符fd_in复制到目标文件描述符fd_out,覆盖目标文件请求范围内的任何数据。
以下语义适用于off_in,类似的语句适用于off_out:
- *
- 如果off_in为NULL,则从文件偏移量开始从fd_in读取字节,并通过复制的字节数来调整文件偏移量。
- *
- 如果off_in不为NULL,则off_in必须指向一个缓冲区,该缓冲区指定将从fd_in读取字节的起始偏移量。 fd_in的文件偏移不会更改,但是off_in会适当调整。
fd_in和fd_out可以引用同一文件。如果它们引用相同的文件,则不允许源和目标范围重叠。
提供flags参数以允许将来扩展,当前必须将其设置为0。
返回值
成功完成后,copy_file_range()将返回文件之间复制的字节数。这可能小于最初请求的长度。如果fd_in的文件偏移量等于或大于文件末尾,则不会复制任何字节,并且copy_file_range()返回零。
发生错误时,copy_file_range()返回-1,并且将errno设置为指示错误。
错误说明
- EBADF
- 一个或多个文件描述符无效。
- EBADF
- fd_in不开放供阅读;或fd_out未打开以进行写入。
- EBADF
- 为文件描述符fd_out引用的打开文件描述(请参见open(2))设置O_APPEND标志。
- EFBIG
- 试图在超过内核支持的最大文件偏移量的位置写入。
- EFBIG
- 试图写一个超出允许的最大文件大小的范围。在文件系统实现之间,最大文件大小有所不同,并且可能与允许的最大文件偏移量不同。
- EFBIG
- 试图写超出进程的文件大小资源限制的文件。这也可能导致进程接收到SIGXFSZ信号。
- EINVAL
- flags参数不为0。
- EINVAL
- fd_in和fd_out引用相同的文件,并且源范围和目标范围重叠。
- EINVAL
- fd_in或fd_out都不是常规文件。
- EIO
- 复制时发生低级I / O错误。
- EISDIR
- fd_in或fd_out都引用目录。
- ENOMEM
- 内存不足。
- ENOSPC
- 目标文件系统上没有足够的空间来完成复制。
- EOVERFLOW
- 请求的源或目标范围太大,无法以指定的数据类型表示。
- EPERM
- fd_out指的是一个不可变的文件。
- ETXTBSY
- fd_in或fd_out都指向活动的交换文件。
- EXDEV
- fd_in和fd_out引用的文件不在同一已挂载的文件系统上(Linux 5.3之前的版本)。
版本
系统调用copy_file_range()首次出现在Linux 4.5中,但是glibc 2.27在不可用时提供了用户空间仿真。
5.3中对内核实现进行了重大修改。澄清了未明确定义的API区域,并且比早期内核更严格地检查了API边界。应用程序应针对5.3内核的行为和要求。
Linux 5.3中首次引入了对跨文件系统副本的支持。尝试跨文件系统复制时,较早的内核将返回-EXDEV。
遵循规范
copy_file_range()系统调用是非标准的Linux和GNU扩展。
备注
如果fd_in是稀疏文件,则copy_file_range()可能会扩展请求范围内存在的任何漏洞。用户可以受益于循环调用copy_file_range()并使用lseek(2)SEEK_DATA和SEEK_HOLE操作来查找数据段的位置。
copy_file_range()为文件系统提供了实现"复制加速"技术的机会,例如使用reflinks(即两个或多个共享共享指向相同的写时复制磁盘块的指针的inode)或服务器端复制(在NFS)。
示例
#define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/syscall.h> #include <unistd.h> /* On versions of glibc before 2.27, we must invoke copy_file_range() using syscall(2) */ static loff_t copy_file_range(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) { return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); } int main(int argc, char **argv) { int fd_in, fd_out; struct stat stat; loff_t len, ret; if (argc != 3) { fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]); exit(EXIT_FAILURE); } fd_in = open(argv[1], O_RDONLY); if (fd_in == -1) { perror("open (argv[1])"); exit(EXIT_FAILURE); } if (fstat(fd_in, &stat) == -1) { perror("fstat"); exit(EXIT_FAILURE); } len = stat.st_size; fd_out = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd_out == -1) { perror("open (argv[2])"); exit(EXIT_FAILURE); } do { ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0); if (ret == -1) { perror("copy_file_range"); exit(EXIT_FAILURE); } len -= ret; } while (len > 0 && ret > 0); close(fd_in); close(fd_out); exit(EXIT_SUCCESS); }
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。