Есть ли функция POSIX для копирования файла?

Посмотрев на некоторые функции ОС, я обнаружил, что в системах POSIX у вас есть вызовы функций C, такие как unlink() для удаления файлов, link() для создания жестких ссылок на файлы, symlink() для создания символических файлов, rename() - переместить файл, но... где функция copy() файла?

Я знаю, что обычным способом является просто открыть исходный файл, прочитать его содержимое, открыть файл назначения и выгрузить их там. Но почему я не могу найти такую ​​функцию полезности, учитывая все предыдущие?

Ответ 1

Говоря о "копировании файла", существуют две семантики:

  • Deep Copy - создание нового файла, содержащего копию всех данных/метаданных, связанных с файлом, однако файловая система структурирует это
  • Неверная копия - создание новой записи/файла каталога, ссылающейся на одни и те же данные (и возможные некоторые или все метаданные) в качестве исходного файла

В файловых системах Windows/DOS традиционно не было механизма "мелкой копии", но UN * X всегда имел форму жестких ссылок.

Итак, POSIX/UN * X имеет системный вызов link(2) - для установления новой ссылки на существующие "данные файла" под новым именем - т.е. делать мелкой копии.

Системный вызов с "глубокой копией" имеет смысл только в том случае, если существует механизм "быстрой глубокой копии" - например, в тех случаях, когда подкладочная файловая система реализует что-то вроде дедупликации для клонирования на уровне файлов.

В противном случае такая функция должна "деградировать" (вернуться к) реализации библиотеки.

Механизм UN * X, позволяющий использовать что-то в качестве файловой системы, так как это ioctl(), "кухонная раковина расширяемости ввода-вывода". Например, как использовать этот механизм, если он доступен, для копирования файлов, см. эту статью GNU coreutils с запросом на расширение, чтобы использовать клонирование файлов на BTRFS.

Учитывая, что Windows < CopyFile на самом деле CopyFileEx без обратного вызова, я сильно сомневаюсь, что это действительно системный вызов; это функция полезности. Для Wine Windows Emulator вы можете проверить исходную реализацию kernel32.dll, найти CopyFileEx в источниках Wine, dlls/kernel32/path.c для примера, как это можно сделать.
Разборка/декомпиляция Фактическая kernel.dll Windows не разрешена при лицензировании Microsoft, поэтому я не могу юридически утверждать, что сама Windows делает то же самое, т.е. CopyFile - это реализация пользовательской среды, а не системный вызов.

Сравнивать Windows и UN * X снова здесь... не все в UN * X libc - это системный вызов, поэтому команды UN * X различают между разделами 2 (вызовы sys) и разделом 3 (интерфейсы библиотеки времени выполнения), То же самое верно для функций в kernel.dll в Windows - некоторые из них являются "прямой транзитной", в то время как другие - более сложные "служебные функции", не реализованные с помощью единого системного вызова.

Ответ 2

Я попытался запустить strace в команде cp в Linux и фактически открыть оба файла, и он читает из одного файла и записывает в другой в блоках из 32768 байт:

...
stat64("log", {st_mode=S_IFREG|0644, st_size=352, ...}) = 0
stat64("copied", 0xbf99e1c0)              = -1 ENOENT (No such file or directory)
open("log", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=352, ...}) = 0
open("copied", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0644) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
read(3, "2012-04-27 22:26:55-0400 [-] Log"..., 32768) = 352
write(4, "2012-04-27 22:26:55-0400 [-] Log"..., 352) = 352
read(3, "", 32768)                      = 0
close(4)                                = 0
close(3)                                = 0
...

Так что да, там нет cp syscall. Надеюсь, это поможет вам.

Ответ 3

Вы не можете найти функцию утилиты для копирования файла, потому что нет необходимости в ней так же; он может быть построен из "запасных частей". Функции типа unlink() и symlink() не могут быть построены в терминах других функций, тогда как такие функции, как гипотетический copy_file(), могут (поэтому вы должны).

Учитывая два открытых потока файлов, f1 для чтения и f2 для записи, вы можете использовать:

void fcopy(FILE *f1, FILE *f2)
{
    char            buffer[BUFSIZ];
    size_t          n;

    while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
    {
        if (fwrite(buffer, sizeof(char), n, f2) != n)
            err_syserr("write failed\n");
    }
}

Функция err_syserr() предназначена для сообщений об ошибках, включая строку, переданную в качестве аргумента, и сообщение об ошибке, указанное в errno *; он не возвращается. BUFSIZ определяется в <stdio.h>, но вы можете использовать большее значение. Вы можете предпочесть не сообщать об ошибке, но при возврате функции 0 при успешном завершении и -1 при любой ошибке.

int fcopy(FILE *f1, FILE *f2)
{
    char            buffer[BUFSIZ];
    size_t          n;

    while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
    {
        if (fwrite(buffer, sizeof(char), n, f2) != n)
            return -1;
    }
    return 0;
}

Обратите внимание, что поскольку функция не открывает файлы, она также не закрывает их. Это означает, что вы можете использовать его для объединения нескольких входных файлов в один выходной файл, например. Вы можете использовать функцию-оболочку, чтобы открыть файл для чтения, а другой для записи.

* Фактически, err_syserr() - это функция типа printf(), которая принимает строку формата и другие аргументы, а затем сообщает об этом сообщение об ошибке, как описано и завершает.

Ответ 4

Системы POSIX не предоставляют системные вызовы, такие как функция CopyFile в Win32, поэтому не тратьте время на поиск функции Copy_file(). Если вы не хотите изобретать колесо, я думаю, что неплохо использовать fork() для создания нового процесса и вызова execl(). Вот так:

execl("/bin/cp", "-p", src_file, des_file, (char *)0);