Pull to refresh

Comments 20

Это не defensive programming.

Все IO-операции могут приводить к сбоям и это задача программиста предоставлять инвариант с обработкой этого файла. Ровно так же, как сетевой программист должен быть готов к странным вещам по сети, программист, работающий с файлами, должен тоже быть готов к неожиданностям.
В моем понимании неожиданностей вообще не должно быть (в идеале). Т.к. без понимания причин сделать корректную обработку «неожиданности» вряд ли получится.
Вы правы. Но «неожиданность» это или инвариант — зависит от программиста. Например, в любой момент файловая система может оказаться в R/O. Или вообще, начать отсутствовать, потому что пользователь флешку вынул.

Хороший стиль программирования (хороший — приводящий к наименьшему числу ошибок) требует, чтобы ошибки и данные обрабатывались одним и тем же кодом. Использование алгебраических типов данных (Option, Result, Maybe) даёт этот эффект почти бесплатно. Использование исключений, наоборот, всё портит и делает код обработки ошибок «отдельным миром».

А что у вас с шарингом при открытии файла, кстати?
С сетевым? Нет его, все локально происходит.
С шарингом открытия файлов. У CreateFile есть соответствующий параметр
Тот, что по умолчанию в с++ в высокоуровневой работе с файлами, где этого параметра нет.
В винде много кто открывает файлы — индексатор, антивирусы и т.д. Нужно переименовывать файл перед удалением, наверное
The DeleteFile function can be used to delete a file on close. A file cannot be deleted until all handles to it are closed. If a file cannot be deleted, its name cannot be reused. To reuse a file name immediately, rename the existing file.
Да, рабочий подход. Переименование сработает даже если файл открыт :) Все же интересно, сколько людей реально эту ситуацию учитывают, когда пишут код.
UFO just landed and posted this here
У вас этап «Удаляем его» вообще лишний. Под Windows обычно просто используют MoveFileEx с флагом MOVEFILE_REPLACE_EXISTING. Заодно и оригинальный файл не потеряете в случае сбоя и в случает с конкуренцией меньше багов. Короче, всегда старайтесь делать операции атомарными, если это возможно.
Проблема в том, что MoveFileEx точно также выдаст access denied.
А вы выставляете флаг FILE_SHARE_DELETE при открытии?
Если да и у вас по прежнему не отрабатывает MoveFileEx с флагом, тогда вам ничего не остается как искать проблему в своем коде. Дело в том, что вот это место:
Решение довольно простое — открываем файл, читаем записи одну за другой, нужные нам записываем во временный файл. Закрываем файл. Удаляем его. Переименовываем временный в оригинальный. Настолько все просто, что даже код приводить не буду.
как раз самое интересное и проблема именно там.
MoveFileEx сначала удаляет файл, потом переименовывает. Если удалить не получается, например, когда файл открыт кем-то другим, то переименовать тоже не получится. FILE_SHARE_DELETE здесь никак не поможет.
Только если стороннее приложение, типа explorer.exe открывает без флага FILE_SHARE_DELETE. Но тогда это просто ошибка приложения, настройки системы, расширений и т.д. С таким же успехом снаружи вам могут прописать что-то в файл, создать такой же, переименовать… К этому нужно быть готовым и писать swap так, что бы операция была индемпотентна. В любом случае, для вашего приложения это невозможность произвести операцию и WinAPI тут не причем.
Виновник нашелся благодаря SysInternals и их ProcessMonitor.

На мой взгляд немаловажной частью проблемы понимания этой ситуации является усечение более информативных NT Status'ов нативного API до более обобщенных Win32 кодов.


https://web.archive.org/web/20150317121919/https://support.microsoft.com/en-us/kb/113996:


WINDOWS NT STATUS CODE                  WIN32 ERROR CODE
------------------------------------------------------------------
STATUS_CANNOT_DELETE                    ERROR_ACCESS_DENIED
STATUS_FILE_DELETED                     ERROR_ACCESS_DENIED
STATUS_FILE_RENAMED                     ERROR_ACCESS_DENIED
STATUS_DELETE_PENDING                   ERROR_ACCESS_DENIED
И это тоже. Вот даже не могу себе представить, нафига им так делать было?

Так же в ключе темы публикации интересно то, что (вероятно для WSL) Microsoft все же добавила новую POSIX-семантику удаления файлов (https://docs.microsoft.com/windows-hardware/drivers/ddi/content/ntddk/ns-ntddk-_file_disposition_information_ex):


When FILE_DISPOSITION_POSIX_SEMANTICS is not set, a file marked for deletion is not actually deleted until all open handles for the file have been closed and the link count for the file is zero. When FILE_DISPOSITION_POSIX_SEMANTICS is set, the link is removed from the visible namespace as soon as the POSIX delete handle has been closed, but the file’s data streams remain accessible by other existing handles until the last handle has been closed. That is, applications that already had the file open can still use their handle to read/write even though the name they used to open it is gone and the file's link count may have reached zero.
If the file is being deleted at user request, using POSIX semantics allows the system to delete the file as requested, but also allows any process with an open handle to continue to access the file's data as long as the handle is open.

Если я правильно понимаю POSIX-семантику, то создание нового файла поверх удаленного должно быть возможным сразу, а не по закрытию последнего описателя на него.
Проверил: нет, FILE_DISPOSITION_POSIX_SEMANTICS не позволяет создавать файлы поверх удаляемого.

Встречалась похожая ситуация, когда моя консольная утилитка не могла удалить папку с ошибкой «папка не пустая». Один раз лично наблюдал эту ошибку, при попытке удалить папку Far'ом, повторная попытка увенчалась успехом. Грешил на антивирус, но т.к. натыкался на нее раз в пару месяцев, так и не смог поймать злоумышленника.

Видимо, именно поэтому git checkout при работающем докере регулярно обваливается с permission denied и не может создать файлы из новой ветки?

Sign up to leave a comment.

Articles