Запись файлов на USB-накопитель вызывает повреждение/блокировку файлов при неожиданном удалении

Я пишу фоновое приложение для циклического копирования файлов на USB-накопитель с установленной политикой «Оптимизировать для быстрого удаления». Однако, если флешка удаляется на полпути в ходе этого процесса (в частности, в вызове WriteFile() ниже, который возвращает ФАЙЛ ОШИБКИ НЕ НАЙДЕН), приложение зависает, накопитель становится постоянно недоступным для любого другого приложения, и ПК нельзя выключить/выключить. вышел из системы / перезапустился и т. д. В результате все запущенные экземпляры Windows Explorer также зависают.

Я проследил проблему до вызова CloseHandle(), сделанного после того, как палка была удалена, и возникла вышеуказанная ошибка. Как будто CloseHandle() где-то в драйвере блокируется на неопределенный срок, потому что палки больше нет? Во всяком случае, мне удалось обойти эту проблему, просто пропустив вызов CloseHandle(), если WriteFile() возвращает ФАЙЛ ОШИБКИ НЕ НАЙДЕН. Однако это приводит к другой проблеме, когда время от времени файл безвозвратно повреждается, и единственный способ исправить это — использовать chkdsk или переформатировать флешку.

Обратите внимание, что это происходит только в XP (SP2 и 3), Vista, похоже, не страдает от этой проблемы. Далее следует фрагмент кода:

HANDLE hFile = CreateFile(szFile, 
                          GENERIC_WRITE, 
                          FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
                          NULL,
                          CREATE_ALWAYS,
                          FILE_FLAG_WRITE_THROUGH,
                          NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
    if (!WriteFile(hFile, pBuffer, dwBufferSize, &dwWritten))
    {
        int nLastError = GetLastError();
    }

    // If usb stick is removed during WriteFile(), ERROR_FILE_NOT_FOUND usually      results.
    // If handle is closed at this point then drive is inaccessible.
    // If CloseHandle() is skipped, then file corruption occurs instead
    if (nLastError != ERROR_FILE_NOT_FOUND)
    {
        CloseHandle(hFile);
    }
}

Я пробовал почти все комбинации флагов для CreateFile(), но все безрезультатно. Кто-нибудь видел это раньше или есть хорошие идеи, как избежать возникновения любой из двух проблем. Является ли то, что я вижу, проблемой драйвера, которая была незаметно устранена в Vista?

Спасибо за любую помощь.


person Community    schedule 25.03.2009    source источник


Ответы (4)


Как будто CloseHandle() где-то в драйвере блокируется на неопределенный срок, потому что палки больше нет?

Звучит разумно. CloseHandle() в конечном итоге выдаст IRP файловой системы, и вы не используете неблокирующий ввод-вывод, так что IRP будет синхронным, но похоже, что фактическая файловая система внезапно исчезла из-под драйвера файловой системы, что IRP никогда не завершается. Это означает, что вы набиты - вызов функции пользовательского режима, который приводит к выдаче IRP файловой системы, никогда не вернется.

Попробуйте использовать неблокирующий ввод-вывод - это поможет вам обойти эту проблему, по крайней мере, с точки зрения отсутствия зависаний. Вы по-прежнему будете сталкиваться с потерей ресурсов и т.п., поскольку IRP по-прежнему будет передаваться и почти наверняка не вернется обратно, но, по крайней мере, вы не будете блокировать его.

Кстати, я бы сказал, что «оптимизация для быстрого удаления» предназначена для уменьшения объема кэширования, которое происходит, и, возможно, для влияния на порядок записи в файловую систему, чтобы уменьшить вероятность повреждения; Я крайне сомневаюсь, что это предназначено для сохранения файловой системы в случае сбоя файловой системы во время записи!

Не удивляйтесь, что это убивает файловую систему.

person Community    schedule 25.03.2009

Похоже, это проблема с драйвером.

Вы должны освободить все дескрипторы для драйвера, чтобы позволить ему очистить себя, и окна, чтобы выгрузить его. Если вы этого не сделаете, драйвер будет думать, что он по-прежнему несет ответственность за устройство, даже если оно изменилось.

Вы не можете избежать этой проблемы в пользовательском режиме.

Отказ от дескриптора просто переносит проблему на более поздний этап (например, выход из вашей программы, чтобы окна пытались закрыть все ваши заброшенные открытые дескрипторы).

person Christopher    schedule 25.03.2009

О проблеме с зависанием: вы можете создать отдельный поток для записи, контролировать процесс записи из основного потока и прерывать поток записи, если он занимает подозрительно много времени. Другими словами: реализуйте запись асинхронно и ищите тайм-аут.

person Adrian Grigore    schedule 25.03.2009
comment
Спасибо за ответ. Это действительно решило бы проблему зависания моего приложения, но тогда ущерб уже нанесен смонтированному диску в файловой системе. Он по-прежнему будет недоступен для любого другого приложения, и ПК будет зависать при выключении / выходе из системы и т. д. - person ; 25.03.2009
comment
Никогда не прерывайте потоки. Нет случаев, когда это могло бы помочь. Вы рискуете только «государственной коррупцией» и т. д. - person Christopher; 25.03.2009
comment
Что еще можно сделать, если поток завис? Я не говорю, что это идеальное решение, но прерывание потока и риск повреждения состояния IMO определенно лучше, чем зависание всего приложения. - person Adrian Grigore; 25.03.2009
comment
@AdrianGrigore: вы можете оставить поток в ожидании завершения операции драйвера. - person Ben Voigt; 13.11.2014

Может ли быть проблема с драйвером файловой системы, а не с драйвером оборудования? Вы можете обнаружить, что если вы используете NTFS, проблема исчезнет.

person Community    schedule 26.03.2009