Побайтовое копирование файлов c
Аннотация: В этой лекции мы снова вернемся к обработке файлов (перенос, копирование, удаление). Научимся исправлять дату и время копии, использовать компоненты, показывающие прогресс копирования файла.
Создайте новый проект. Форму сразу же переименуйте в fMain , в свойстве Caption напишите "Работа с файлами" а проект сохраните в новую папку под именем "FileManager". Стиль формы сделайте bsDialog , а положение формы – poDesktopCenter .
Далее, установите на форму компонент Label , в свойстве Caption которого напишите "Что:". Рядом установите Edit , и удалите из него текст. В этот Edit мы будем заносить исходный файл , или что мы собираемся копировать или переносить.
Ниже установите еще по одному Label и Edit . У Label напишите "Куда:", а у Edit удалите текст. Оба Edit немного расширьте, чтобы могли уместиться имена файлов с длинными адресами.
Ниже установите еще один Label , и в нем напишите: " Размер файла :".
Ниже будут две кнопки – "Копировать" и "Перенести".
Еще ниже установим индикатор копирования. Обычно для таких целей устанавливают индикатор ProgressBar с вкладки Win32 . У него за показ процентного отношения отвечает свойство Position . Установите этот компонент и попробуйте этому свойству присваивать разные значения от 0 до 100, чтобы посмотреть, как изменяется внешний вид компонента. Однако мы для этих целей воспользуемся другим компонентом, поэтому удалите ProgressBar и перейдите на вкладку Samples . Там выберите компонент Gauge , и установите его на форму. Как видите, по умолчанию Gauge имеет квадратную форму. Свойству Height присвойте значение 30, а свойству Width – 270. Таким образом, мы придали Gauge горизонтальную направленность. Этот компонент не только выглядит привлекательней предыдущего, но также показывает процент выполненного действия! За этот процент отвечает свойство Progress , попробуйте присвоить ему разные значения от 0 до 100. Каким именно компонентом пользоваться в таких случаях – дело вкуса каждого программиста. Но вы должны знать, что в стандартной поставке Delphi есть два компонента, которые выполняют это действие. Верните Progress в ноль. Вы, наверное, уже успели заметить, что компонент по умолчанию окрашивается в черный цвет. Это немного мрачно, поэтому в свойстве ForeColor выберите темно-синий цвет.
Для того, чтобы пользователь не вводил много лишнего текста, облегчим ему работу и установим два диалога – OpenDialog и SaveDialog . А для их вызова справа от компонентов Edit установите по одной кнопочке, ширину и высоту которых сделайте такой же, как высота Edit , то есть, 21, чтобы кнопочки получились квадратными. В результате, компонент Edit и стоящая справа от него квадратная кнопочка кажутся одним элементом интерфейса. У вас должна получиться форма примерно с таким интерфейсом:
Впрочем, вы самостоятельно можете разработать дизайн программы, главное, чтобы интерфейс был понятным пользователю, и компоненты на форме располагались красиво.
Функция переноса файла отличается от функции копирования только тем, что в конце работы мы удалим исходный файл , следовательно, для переноса будем вызывать процедуру копирования, чтобы не писать дважды один и тот же код.
Для того, чтобы не путаться при написании кода, переименуйте верхний Edit в Otkuda , а нижний – в Kuda (свойство Name ). Так мы точно будем знать, где и что у нас находится.
Теперь займемся написанием кода программы. Для начала объявим процедуру, которая будет побайтово копировать файл . Ранее мы уже вводили в программы собственные функции и процедуры. Мы их нигде не объявляли, а просто описывали выше того места, откуда в дальнейшем вызывали. Этот способ имел один минус – такая процедура не являлась частью объекта Форма, и для того, чтобы обратиться к какому-нибудь компоненту, нам приходилось вначале обращаться к форме, например:
Однако собственную процедуру или функцию можно сделать частью формы, если вначале ее объявить. Делается это в разделе private , сразу под комментарием:
Теперь нашу процедуру можно вызывать из любой части формы. Если бы мы описали ее в разделе public , то ее можно было бы вызывать и из других модулей.
Мы объявили эту процедуру в объекте TfMain , чтобы можно было обращаться к ней из любого места программы и напрямую использовать компоненты формы. Саму процедуру напишем в разделе implementation , после директивы компилятору dfm > . Обратите внимание, что имя процедуры должно начинаться с обращения к имени формы:
Итак, что нового для нас в этом коде? Во-первых, директивы компилятору. Директива отключает обработку компилятором ошибок ввода-вывода (Input-Output), нам это необходимо, чтобы получить результат – была ошибка или нет. Ниже мы вновь включаем эту обработку: .
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Копирует существующий файл в новый файл.
Copy(String, String)
Копирует существующий файл в новый файл. Перезапись файла с тем же именем не разрешена.
Параметры
Имя целевого файла. Это не может быть имя каталога или имя существующего файла.
Исключения
У вызывающего объекта отсутствует необходимое разрешение.
sourceFileName или destFileName представляет собой строку нулевой длины, строку, содержащую только пробелы, или строку, содержащую один или несколько недопустимых символов. Вы можете запросить недопустимые символы с помощью метода GetInvalidPathChars().
-или- Параметр sourceFileName или destFileName определяет каталог.
Параметр sourceFileName или destFileName имеет значение null .
Указанный путь, имя файла или оба значения превышают максимальную длину, заданную в системе.
В sourceFileName или destFileName указан недопустимый путь (например, ведущий на несопоставленный диск).
Не удалось найти sourceFileName .
-или- Произошла ошибка ввода-вывода.
Параметр sourceFileName или destFileName имеет недопустимый формат.
Примеры
В следующем примере файлы копируются в папку резервного копирования C:\archives\2008. В нем используются две перегрузки Copy метода следующим образом:
Сначала используется перегрузка File.Copy(String, String) метода для копирования текстовых файлов (.txt). В коде показано, что эта перегрузка не позволяет перезаписывать файлы, которые уже были скопированы.
Затем он использует перегрузку File.Copy(String, String, Boolean) метода для копирования изображений (.jpg файлов). В коде показано, что эта перегрузка разрешает перезаписывать уже скопированные файлы.
Перегрузки
Копирует существующий файл в новый файл. Перезапись файла с тем же именем не разрешена.
Копирует существующий файл в новый файл. Перезапись файла с тем же именем разрешена.
Комментарии
Этот метод эквивалентен перегрузке Copy(String, String, Boolean) метода с заданным параметром overwrite false .
Параметры sourceFileName могут destFileName указывать относительные или абсолютные сведения о пути. Относительные сведения о пути интерпретируются относительно текущего рабочего каталога. Сведения о получении текущего рабочего каталога см. в этом методе Directory.GetCurrentDirectory . Этот метод не поддерживает подстановочные знаки в параметрах.
Аннотация: В этой лекции мы снова вернемся к обработке файлов (перенос, копирование, удаление). Научимся исправлять дату и время копии, использовать компоненты, показывающие прогресс копирования файла.
Создайте новый проект. Форму сразу же переименуйте в fMain , в свойстве Caption напишите "Работа с файлами" а проект сохраните в новую папку под именем "FileManager". Стиль формы сделайте bsDialog , а положение формы – poDesktopCenter .
Далее, установите на форму компонент Label , в свойстве Caption которого напишите "Что:". Рядом установите Edit , и удалите из него текст. В этот Edit мы будем заносить исходный файл , или что мы собираемся копировать или переносить.
Ниже установите еще по одному Label и Edit . У Label напишите "Куда:", а у Edit удалите текст. Оба Edit немного расширьте, чтобы могли уместиться имена файлов с длинными адресами.
Ниже установите еще один Label , и в нем напишите: " Размер файла :".
Ниже будут две кнопки – "Копировать" и "Перенести".
Еще ниже установим индикатор копирования. Обычно для таких целей устанавливают индикатор ProgressBar с вкладки Win32 . У него за показ процентного отношения отвечает свойство Position . Установите этот компонент и попробуйте этому свойству присваивать разные значения от 0 до 100, чтобы посмотреть, как изменяется внешний вид компонента. Однако мы для этих целей воспользуемся другим компонентом, поэтому удалите ProgressBar и перейдите на вкладку Samples . Там выберите компонент Gauge , и установите его на форму. Как видите, по умолчанию Gauge имеет квадратную форму. Свойству Height присвойте значение 30, а свойству Width – 270. Таким образом, мы придали Gauge горизонтальную направленность. Этот компонент не только выглядит привлекательней предыдущего, но также показывает процент выполненного действия! За этот процент отвечает свойство Progress , попробуйте присвоить ему разные значения от 0 до 100. Каким именно компонентом пользоваться в таких случаях – дело вкуса каждого программиста. Но вы должны знать, что в стандартной поставке Delphi есть два компонента, которые выполняют это действие. Верните Progress в ноль. Вы, наверное, уже успели заметить, что компонент по умолчанию окрашивается в черный цвет. Это немного мрачно, поэтому в свойстве ForeColor выберите темно-синий цвет.
Для того, чтобы пользователь не вводил много лишнего текста, облегчим ему работу и установим два диалога – OpenDialog и SaveDialog . А для их вызова справа от компонентов Edit установите по одной кнопочке, ширину и высоту которых сделайте такой же, как высота Edit , то есть, 21, чтобы кнопочки получились квадратными. В результате, компонент Edit и стоящая справа от него квадратная кнопочка кажутся одним элементом интерфейса. У вас должна получиться форма примерно с таким интерфейсом:
Впрочем, вы самостоятельно можете разработать дизайн программы, главное, чтобы интерфейс был понятным пользователю, и компоненты на форме располагались красиво.
Функция переноса файла отличается от функции копирования только тем, что в конце работы мы удалим исходный файл , следовательно, для переноса будем вызывать процедуру копирования, чтобы не писать дважды один и тот же код.
Для того, чтобы не путаться при написании кода, переименуйте верхний Edit в Otkuda , а нижний – в Kuda (свойство Name ). Так мы точно будем знать, где и что у нас находится.
Теперь займемся написанием кода программы. Для начала объявим процедуру, которая будет побайтово копировать файл . Ранее мы уже вводили в программы собственные функции и процедуры. Мы их нигде не объявляли, а просто описывали выше того места, откуда в дальнейшем вызывали. Этот способ имел один минус – такая процедура не являлась частью объекта Форма, и для того, чтобы обратиться к какому-нибудь компоненту, нам приходилось вначале обращаться к форме, например:
Однако собственную процедуру или функцию можно сделать частью формы, если вначале ее объявить. Делается это в разделе private , сразу под комментарием:
Теперь нашу процедуру можно вызывать из любой части формы. Если бы мы описали ее в разделе public , то ее можно было бы вызывать и из других модулей.
Мы объявили эту процедуру в объекте TfMain , чтобы можно было обращаться к ней из любого места программы и напрямую использовать компоненты формы. Саму процедуру напишем в разделе implementation , после директивы компилятору dfm > . Обратите внимание, что имя процедуры должно начинаться с обращения к имени формы:
Итак, что нового для нас в этом коде? Во-первых, директивы компилятору. Директива отключает обработку компилятором ошибок ввода-вывода (Input-Output), нам это необходимо, чтобы получить результат – была ошибка или нет. Ниже мы вновь включаем эту обработку: .
Аннотация: В этой лекции мы снова вернемся к обработке файлов (перенос, копирование, удаление). Научимся исправлять дату и время копии, использовать компоненты, показывающие прогресс копирования файла.
Создайте новый проект. Форму сразу же переименуйте в fMain , в свойстве Caption напишите "Работа с файлами" а проект сохраните в новую папку под именем "FileManager". Стиль формы сделайте bsDialog , а положение формы – poDesktopCenter .
Далее, установите на форму компонент Label , в свойстве Caption которого напишите "Что:". Рядом установите Edit , и удалите из него текст. В этот Edit мы будем заносить исходный файл , или что мы собираемся копировать или переносить.
Ниже установите еще по одному Label и Edit . У Label напишите "Куда:", а у Edit удалите текст. Оба Edit немного расширьте, чтобы могли уместиться имена файлов с длинными адресами.
Ниже установите еще один Label , и в нем напишите: " Размер файла :".
Ниже будут две кнопки – "Копировать" и "Перенести".
Еще ниже установим индикатор копирования. Обычно для таких целей устанавливают индикатор ProgressBar с вкладки Win32 . У него за показ процентного отношения отвечает свойство Position . Установите этот компонент и попробуйте этому свойству присваивать разные значения от 0 до 100, чтобы посмотреть, как изменяется внешний вид компонента. Однако мы для этих целей воспользуемся другим компонентом, поэтому удалите ProgressBar и перейдите на вкладку Samples . Там выберите компонент Gauge , и установите его на форму. Как видите, по умолчанию Gauge имеет квадратную форму. Свойству Height присвойте значение 30, а свойству Width – 270. Таким образом, мы придали Gauge горизонтальную направленность. Этот компонент не только выглядит привлекательней предыдущего, но также показывает процент выполненного действия! За этот процент отвечает свойство Progress , попробуйте присвоить ему разные значения от 0 до 100. Каким именно компонентом пользоваться в таких случаях – дело вкуса каждого программиста. Но вы должны знать, что в стандартной поставке Delphi есть два компонента, которые выполняют это действие. Верните Progress в ноль. Вы, наверное, уже успели заметить, что компонент по умолчанию окрашивается в черный цвет. Это немного мрачно, поэтому в свойстве ForeColor выберите темно-синий цвет.
Для того, чтобы пользователь не вводил много лишнего текста, облегчим ему работу и установим два диалога – OpenDialog и SaveDialog . А для их вызова справа от компонентов Edit установите по одной кнопочке, ширину и высоту которых сделайте такой же, как высота Edit , то есть, 21, чтобы кнопочки получились квадратными. В результате, компонент Edit и стоящая справа от него квадратная кнопочка кажутся одним элементом интерфейса. У вас должна получиться форма примерно с таким интерфейсом:
Впрочем, вы самостоятельно можете разработать дизайн программы, главное, чтобы интерфейс был понятным пользователю, и компоненты на форме располагались красиво.
Функция переноса файла отличается от функции копирования только тем, что в конце работы мы удалим исходный файл , следовательно, для переноса будем вызывать процедуру копирования, чтобы не писать дважды один и тот же код.
Для того, чтобы не путаться при написании кода, переименуйте верхний Edit в Otkuda , а нижний – в Kuda (свойство Name ). Так мы точно будем знать, где и что у нас находится.
Теперь займемся написанием кода программы. Для начала объявим процедуру, которая будет побайтово копировать файл . Ранее мы уже вводили в программы собственные функции и процедуры. Мы их нигде не объявляли, а просто описывали выше того места, откуда в дальнейшем вызывали. Этот способ имел один минус – такая процедура не являлась частью объекта Форма, и для того, чтобы обратиться к какому-нибудь компоненту, нам приходилось вначале обращаться к форме, например:
Однако собственную процедуру или функцию можно сделать частью формы, если вначале ее объявить. Делается это в разделе private , сразу под комментарием:
Теперь нашу процедуру можно вызывать из любой части формы. Если бы мы описали ее в разделе public , то ее можно было бы вызывать и из других модулей.
Мы объявили эту процедуру в объекте TfMain , чтобы можно было обращаться к ней из любого места программы и напрямую использовать компоненты формы. Саму процедуру напишем в разделе implementation , после директивы компилятору dfm > . Обратите внимание, что имя процедуры должно начинаться с обращения к имени формы:
Итак, что нового для нас в этом коде? Во-первых, директивы компилятору. Директива отключает обработку компилятором ошибок ввода-вывода (Input-Output), нам это необходимо, чтобы получить результат – была ошибка или нет. Ниже мы вновь включаем эту обработку: .
Я ищу Unix-эквивалент CopyFile Win32 , я не хочу изобретать колесо, написав свою собственную версию.
Чтобы не изобретать колесо, компилируйте GNU coreutils, AFAIK у него есть статическая библиотека для копирования файлов в его дереве сборки, используемая cp и другими. Поддерживает разреженность и корову btrfs
Нет необходимости ни вызывать непереносимые API-интерфейсы, например sendfile , ни командовать внешними утилитами. Тот же метод, который работал в 70-х годах, работает и сейчас:
@Caf: OMG . goto . :) Ваш код в любом случае более разумный, чем мой . ;) Старый цикл с чтением / записью самый переносимый . +1 от меня .
Я считаю, что контролируемое использование goto может быть полезным для объединения пути обработки ошибок в одном месте.
Не подходит для общего назначения. Копия файла - это больше, чем просто поток данных. Как насчет разреженных файлов или расширенных атрибутов? Вот еще раз, почему Windows API такой уродливый, как и Linux.
Файлы @Lothar Unix концептуально представляют собой просто последовательность байтов. Метаданные, такие как разрешения, ACL и т. Д., Обрабатываются перпендикулярно фактическому копированию данных. Так и должно быть. Форматы файлов, специфичные для приложения, являются проблемой приложения. Так и должно быть.
В API нет встроенной эквивалентной функции CopyFile. Но sendfile можно использовать для копирования файла в режиме ядра, что является более быстрым и лучшим решением (по многим причинам), чем открытие файла, зацикливание на нем для чтения в буфер и запись вывода в другой файл.
Начиная с версии ядра Linux 2.6.33, ограничение, требующее, чтобы вывод sendfile был сокетом, было снято, и исходный код будет работать как в Linux, так и - однако, начиная с OS X 10.9 Mavericks, sendfile в OS X теперь требуется, чтобы вывод был сокет и код работать не будет!
Следующий фрагмент кода должен работать на большинстве OS X (начиная с версии 10.5), (Free) BSD и Linux (начиная с версии 2.6.33). Реализация является «нулевой копией» для всех платформ, что означает, что все это делается в пространстве ядра, и отсутствует копирование буферов или данных в пользовательское пространство и из него. Практически лучшая производительность, которую вы можете получить.
РЕДАКТИРОВАТЬ : Заменено открытие пункта назначения на вызов, creat() как мы хотим, чтобы флаг O_TRUNC был указан. См. Комментарий ниже.
Согласно странице руководства, выходной аргумент sendfile должен быть сокетом. Вы уверены, что это работает?
Для Linux, Джей Шатун прав - out_fd из sendfile может быть обычным файлом в ядрах 2.4, но теперь он должен поддерживать sendpage внутренний API ядра (который по существу означает трубу или гнездо). sendpage реализован по-разному в разных UNIX - для него нет стандартной семантики.
Прототип под Linux отличается от OSX, поэтому вы можете подумать (и я тоже так думал), что, когда я увидел вашу реализацию и дополнительные параметры для файла отправки . он зависит от платформы - о чем стоит помнить!
fyi - вы можете сэкономить много работы с if (PathsMatch (source, destination)) return 1; / * где PathsMatch - подходящая процедура сравнения путей для локали * /, в противном случае я предполагаю, что второе открытие завершится ошибкой.
+1 man sendfile говорит, что с версии 2.6.33 это снова поддерживается. sendfile() превосходит, CopyFile() поскольку допускает смещение. Это полезно для удаления информации заголовка из файла.
Просто используйте fork / execl для запуска cp, который сделает всю работу за вас. У этого есть преимущества перед системой в том, что он не подвержен атакам Bobby Tables, и вам не нужно очищать аргументы в той же степени. Кроме того, поскольку system () требует, чтобы вы сколотили аргумент команды, у вас вряд ли возникнет проблема переполнения буфера из-за небрежной проверки sprintf ().
Преимущество прямого вызова cp вместо его записи заключается в том, что вам не нужно беспокоиться об элементах целевого пути, существующих в месте назначения. Это утомительно и чревато ошибками.
Я написал этот пример на ANSI C и исключил только самую незначительную обработку ошибок, кроме того, что это простой код.
+1 за еще одну длинную, детальную, утомительную работу. Действительно заставляет вас оценить "векторную" / списковую форму system () в Perl. Хм. Может быть, неплохо было бы иметь системную функцию с массивом argv.
. в конце концов, это было реализовано 17 лет назад в glibc и являлось стандартной функцией за 10 лет до того, как был написан ваш ответ ..
Другой вариант функции копирования с использованием обычных вызовов POSIX и без какого-либо цикла. Код, вдохновленный вариантом ответа кафе с буферной копией. Предупреждение: использование mmap может легко выйти из строя в 32-битных системах, в 64-битных системах опасность менее вероятна.
(Я понимаю, что это старый вопрос, но . ) Что произойдет с mmap, если размер отображаемого файла очень велик по сравнению с размером доступной памяти и файла подкачки? Будет ли зависать система при нехватке памяти / подкачки?
Отображение файла в диапазон адресов процесса само по себе не занимает никакой памяти. Это как если бы вы сказали, что ваш файл теперь является частью области подкачки. Это означает, что когда вы обращаетесь к адресу в сопоставленном файле, он сначала генерирует ошибку страницы, так как в памяти ничего нет. Затем ОС загружает соответствующую страницу по этому адресу с диска и восстанавливает управление процессом. Если нет доступной памяти, тогда ОС просто освободит некоторые другие отображенные страницы от любого другого процесса; в приоритетных чистых страницах (т.е. которые не нужно записывать на диск), но также и грязных страницах. =>
Подкачка происходит, когда шаблон доступа к отображаемым страницам превышает объем физической памяти в системе, и ей все время приходится читать и записывать страницы. mmap можно рассматривать как не что иное, как просто увеличение области подкачки систем. mmap с опцией MAP_SHARED также можно рассматривать как способ сделать файловый кеш доступным для процесса.
Поэтому, если вы используете mmap для большого файла, а затем получаете доступ к нему, а размер файла, к которому вы обращаетесь, больше, чем ваша реальная память, ОС начнет выгружать другие процессы. Если это произойдет слишком часто, операционная система начнет работать с подкачкой. Я хочу сказать, что с файлами, большими по сравнению с памятью + своп, вы должны подумать о размере данных mmap, к которым осуществляется доступ, чтобы не вызывать проблем.
Есть способ сделать это, не прибегая к system вызову, вам нужно включить обертку примерно так:
В приведенном выше примере (проверка ошибок опущена!) Используются open , close и sendfile .
Я не уверен на 100% насчет прототипа sendfile, думаю, я ошибся в одном из параметров . имейте это в виду . :)
У вас есть состояние гонки - файл, который вы открыли, fdSource и файл, который у вас есть stat()ed , не обязательно совпадают.
@caf: Не могли бы вы дать более подробную информацию, когда я смотрю на это, и как может быть состояние гонки? Я внесу соответствующие поправки в ответ . спасибо, что сообщили мне .
tommbieb75: Просто - между open() вызовом и stat() вызовом кто-то другой мог переименовать файл и поместить под это имя другой файл, поэтому вы скопируете данные из первого файла, но используя длину второго.
@caf: Holy moly . почему я не подумал об этом . хорошо замечено . блокировка должна помочь с исходным файлом . молодец, обнаружив это . состояние гонки .. ну, я никогда . как Клинт Иствуд в "Гран Торино" говорит: "JC всю пятницу . "
Один из вариантов - это то, что вы можете использовать system() для выполнения cp . Это просто повторное использование cp(1) команды для выполнения работы. Если вам нужно только сделать еще одну ссылку на файл, это можно сделать с помощью link() или symlink() .
Действительно? Вы бы использовали это в производственном коде? Я не могу придумать веской причины не делать этого, но это не кажется мне чистым решением.
Если вы укажете путь к / bin / cp, вы в относительной безопасности, если только злоумышленнику не удалось скомпрометировать систему до такой степени, что он может внести изменения в произвольные утилиты системной оболочки в / bin. Если они скомпрометировали систему до такой степени, у вас будут гораздо большие проблемы.
Использование системы для запуска команд довольно распространено в unix-land. При соблюдении правил гигиены он может быть достаточно надежным и надежным. В конце концов, команды предназначены для использования именно таким образом.
Побайтовое копирование файлов действительно работает, но в современных UNIX-системах оно медленное и расточительное. Современные UNIX имеют встроенную поддержку «копирования при записи» в файловую систему: системный вызов создает новую запись каталога, указывающую на существующие байты на диске, и байты содержимого файла на диске не затрагиваются, пока одна из копий не будет изменена. , после чего на диск записываются только измененные блоки. Это позволяет практически мгновенно копировать файлы, не используя дополнительных файловых блоков, независимо от размера файла. Например, вот некоторые подробности о том, как это работает в xfs .
В macOS используйте clonefile (2) для мгновенных копий на томах APFS. Это то, что cp -c использует Apple . Документация не совсем ясна, но вполне вероятно, что copyfile (3) with COPYFILE_CLONE также использует это. Оставьте комментарий, если хотите, чтобы я это проверил.
В случае, если эти операции копирования при записи не поддерживаются - если ОС слишком старая, базовая файловая система не поддерживает ее, или из-за того, что вы копируете файлы между разными файловыми системами - вам нужно вернуться к попытке sendfile или в крайнем случае - побайтовое копирование. Но спасти всех много времени и дискового пространства, пожалуйста , дайте FICLONE и clonefile(2) попробовать первым.
Я вижу, что никто еще не упомянул copy_file_range , поддерживаемый по крайней мере в Linux и FreeBSD . Преимущество этого метода в том, что он явно документирует возможность использования методов CoW, таких как рефссылки. Цитата:
copy_file_range() gives filesystems an opportunity to implement "copy acceleration" techniques, such as the use of reflinks (i.e., two or more inodes that share pointers to the same copy-on-write disk blocks) or server-side-copy (in the case of NFS).
FWIW, я не уверен, что старые sendfile могут это сделать. Несколько найденных мной упоминаний утверждают, что это не так. В этом смысле copy_file_range лучше sendfile .
Добавьте несколько проверок ошибок .
В противном случае откройте оба и выполните цикл чтения / записи, но, вероятно, не то, что вы хотите.
ОБНОВЛЕНИЕ для устранения серьезных проблем безопасности:
Вместо использования «system ()» выполните fork / wait и вызовите execv () или execl () в дочернем элементе.
Это не работает для файлов, в имени которых есть пробелы (или кавычки, обратная косая черта, знаки доллара и т. Д.). Я довольно часто использую пробелы в именах файлов.
Хорошо, это швейцарский сыр (см. Обоснованные проблемы безопасности в комментариях в другом месте), но если у вас относительно контролируемая среда, он может иметь некоторое применение.
У вас есть уязвимость внедрения кода оболочки, если вы неправильно обрабатываете символы одинарных кавычек в значениях old или new . Немного больше усилий, чтобы использовать fork и выполнить свой собственный exec, можно избежать всех этих проблем с цитированием.
Да, во многих случаях просто очевидно и неверно. Вот почему я проголосовал за некоторые из наиболее сложных примеров.
Работает на windows и linux.
Хороший вопрос. Связанный с другим хорошим вопросом:
Есть два подхода к «простейшей» реализации cp. Один из подходов использует какую-то функцию системного вызова для копирования файлов - наиболее близкую к C-версии Unix-команды cp. Другой подход использует буфер и функции системного вызова чтения / записи либо напрямую, либо с использованием оболочки FILE.
Вероятно, системные вызовы копирования файлов, которые выполняются исключительно в памяти, принадлежащей ядру, быстрее, чем системные вызовы, которые происходят как в памяти ядра, так и в памяти пользователя, особенно в настройках сетевой файловой системы (копирование между машинами). Но это потребует тестирования (например, с использованием командного времени Unix) и будет зависеть от оборудования, на котором код компилируется и выполняется.
Также вероятно, что кто-то с ОС, не имеющей стандартной библиотеки Unix, захочет использовать ваш код. Тогда вы захотите использовать версию для чтения / записи буфера, поскольку она зависит только от и (и друзей).
Вот пример, который использует функцию copy_file_range из стандартной библиотеки unix для копирования исходного файла в (возможно, несуществующий) целевой файл. Копирование происходит в пространстве ядра.
Он основан на примере, приведенном на странице руководства для моего дистрибутива Ubuntu 20.x Linux для copy_file_range. Проверьте свои справочные страницы с помощью:
Затем нажимайте j или, Enter пока не дойдете до раздела примеров. Или выполните поиск, набрав /example .
Только /Вот пример, который использует только stdlib/stdio . Обратной стороной является использование промежуточного буфера в пользовательском пространстве.
Читайте также: