COPY_FILE_RANGE(2)

ИМЯ
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 изменяется соответствующим образом.

Аргумент flags предназначен для будущих расширений, а пока его значение должно быть равно 0.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении copy_file_range() возвращает количество скопированных между файлами байт. Оно может быть меньше запрашиваемой длины.

В случае ошибки copy_file_range() возвращает -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

EBADF
    Один или оба файловых дескрипторов некорректны; или fd_in не открыт на чтение; или fd_out не открыт на запись; или для открытого файлового описания, на которое ссылается fd_out, установлен флаг O_APPEND.

EFBIG
    Попытка записать в файл, который превышает заданное при реализации ограничение на размер файла или ограничение на размер файла для текущего процесса, или запись в позицию после максимально разрешённого смещения.

EINVAL
    Запрашиваемая область выходит за конец файла-источника; или аргумент flags не равен 0.

EIO Во время копирования возникла низкоуровневая ошибка ввода-вывода.

EISDIR
    Значение fd_in или fd_out указывает на каталог.

ENOMEM
    Не хватает памяти.

ENOSPC
    Недостаточно места на файловой системе назначения для завершения копирования.

EXDEV
    Файлы, на которые ссылаются file_in и file_out, находятся на разным смонтированных файловых системах.

ВЕРСИИ
Системный вызов copy_file_range() впервые появился в Linux 4.5, но если он недоступен, в glibc 2.27 предоставляется эмуляция в пользовательском пространстве.

СООТВЕТСТВИЕ СТАНДАРТАМ
Системный вызов copy_file_range() является нестандартным расширением Linux и GNU.

ЗАМЕЧАНИЯ
Если файл file_in является разреженным (sparse), то copy_file_range() может расширить дыры, существующие в запрашиваемой области. Пользователи могут получить преимущество от вызова copy_file_range() в цикле, и используя операции lseek(2) SEEK_DATA и SEEK_HOLE для поиска расположений сегментов данных.

Вызов copy_file_range() даёт файловым системам возможность реализовать «ускорение копирования», например, использовать ссылочные связи (т. е., две или более инод, использующих общие указатели для одного копирования-при-записи дисковых блоков) или копирование-на-сервере (server-side-copy, в случае использования NFS).

ПРИМЕР
    #define _GNU_SOURCE
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/syscall.h>
    #include <unistd.h>
    / * В версиях glibc до 2.27, мы должны ссылаться на copy_file_range ()
     используя 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, "Использование: %s <источник> <приёмник>\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        fd_in = open(argv[1], O_RDONLY);
        if (fd_in == -1) {
            perror("открытие (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("открытие (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);
        close(fd_in);
        close(fd_out);
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
lseek(2), sendfile(2), splice(2)