COPY_FILE_RANGE - Linux手册页

时间:2019-08-20 17:58:37  来源:igfitidea点击:

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);
}

另外参见

lseek(2),sendfile(2),splice(2)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/