Возможно ли переименование атомарного файла (с перезаписью) в Windows?
В системах POSIX rename(2) предусматривает атомарную операцию переименования, включая перезапись файла назначения, если он существует и если разрешения позволяют.
Есть ли способ получить ту же семантику в Windows? Я знаю о MoveFileTransacted() в Vista и Server 2008, но мне нужно это для поддержки Win2k и выше.
Ключевое слово здесь - атомарное... решение не должно быть в состоянии каким-либо образом потерпеть неудачу, что оставляет операцию в несогласованном состоянии.
Я видел много людей, которые говорили, что это невозможно на win32, но я спрашиваю вас, правда ли это?
Пожалуйста, предоставьте надежные ссылки, если это возможно.
6 ответов
Win32 не гарантирует элементарных операций с метаданными файла. Я бы привел цитату, но ее нет - тот факт, что нет письменной или документально подтвержденной гарантии, значит так много.
Вы должны будете написать свои собственные подпрограммы, чтобы поддержать это. Это прискорбно, но вы не можете ожидать, что win32 обеспечит такой уровень обслуживания - он просто не предназначен для этого.
Увидеть ReplaceFile()
в Win32 ( http://research.microsoft.com/pubs/64525/tr-2006-45.pdf)
В Windows Vista и Windows Server 2008 была добавлена функция атомарного перемещения - MoveFileTransacted()
К сожалению, это не помогает с более старыми версиями Windows.
Начиная с Windows 10 1607, NTFS поддерживает атомарную операцию переименования. Для этого вызовите NtSetInformationFile(..., FileRenameInformationEx, ...) и укажите флаг FILE_RENAME_POSIX_SEMANTICS. Или эквивалентно в Win32 вызовите SetFileInformationByHandle(..., FileRenameInfoEx, ...) и укажите флаг FILE_RENAME_FLAG_POSIX_SEMANTICS.
У вас все еще есть вызов rename() в Windows, хотя я думаю, что гарантии, которые вы хотите, не могут быть сделаны без знания используемой вами файловой системы - никаких гарантий, например, если вы используете FAT.
Однако вы можете использовать MoveFileEx и использовать параметры MOVEFILE_REPLACE_EXISTING и MOVEFILE_WRITE_THROUGH. Последний имеет это описание в MSDN:
Установка этого значения гарантирует, что перемещение, выполненное как операция копирования и удаления, будет сброшено на диск до возврата функции. Сброс происходит в конце операции копирования.
Я знаю, что это не обязательно то же самое, что операция переименования, но я думаю, что это может быть лучшей гарантией, которую вы получите - если она делает это для перемещения файла, она должна для более простого переименования.
В документации MSDN не говорится, какие API являются атомарными, а какие нет, но Найл Дуглас в своем выступлении на Cppcon 2015 заявляет, что единственной атомарной функцией является
с FILE_RENAME_INFO.ReplaceIfExists
установлено значение true. Он доступен, начиная с Windows Vista / 2008 Server.
Найл является автором очень сложной библиотеки LLFIO и экспертом в условиях гонки файловой системы, поэтому я считаю, что если вы пишете алгоритм, в котором атомарность имеет решающее значение, лучше перестраховаться, чем сожалеть, и использовать предложенную функцию, даже если в ней ничего нет.ReplaceFile
в описании говорится, что это не атомарно.
Достаточное количество ответов, но не тот, который я ожидал... У меня было понимание (возможно, неправильное), что MoveFile может быть атомарным, при условии, что правильные звезды выровнены, флаги были использованы, и файловая система была одинаковой в источнике в качестве цели, В противном случае операция может вернуться к файлу [Copy->Delete].
При условии; Я также понимал, что MoveFile - когда он атомарный - просто устанавливает информацию о файле, что также можно сделать здесь: setfileinfobyhandle.
Кто-то выступил с докладом " Гонки по файловой системе", в котором более подробно расскажу об этом. (примерно на 2/3 ниже они говорят об атомном переименовании)
Есть std::rename и начиная с C++17 std::filesystem:: rename. Не указано, что произойдет, если пункт назначения существует сstd::rename
:
Если new_filename существует, поведение определяется реализацией.
Однако для атомарной замены существующих файлов требуется переименование POSIX:
Эта функция rename () эквивалентна для обычных файлов функции, определенной стандартом ISO C. Его включение здесь расширяет это определение, чтобы включить в него действия с каталогами, и определяет поведение, когда новый параметр называет уже существующий файл. Эта спецификация требует, чтобы действие функции было атомарным.
К счастью, std::filesystem::rename
требует, чтобы он вел себя так же, как POSIX:
Перемещает или переименовывает объект файловой системы, идентифицированный old_p, в new_p, как если бы переименованием POSIX
Однако, когда я попытался отладить, оказалось, что std::filesystem::rename
как реализовано VS2019 (по состоянию на март 2020 года) просто вызывает MoveFileEx, который в некоторых случаях не является атомарным. Так что, возможно, когда все ошибки в его реализации будут исправлены, мы увидим переносимый атомарныйstd::filesystem::rename
.
"Соответствующий отрывок из этой исследовательской работы MS: " Под...."
Это говорит о том, что операция является атомарной, но она вводит в заблуждение и подходит только для того, чтобы создать путаницу. Требуется атомарное удаление старого файла и переименование нового файла за один неделимый шаг. Это могут быть две операции метаданных, которые сами по себе являются атомарными, но соединение НЕ атомарно.