Отображение в памяти файла в Windows с атрибутом SHARE (чтобы файл не был заблокирован для удаления) - PullRequest
0 голосов
/ 11 января 2019

Есть ли способ сопоставить содержимое файла в памяти в Windows, который не удерживает блокировку файла (в частности, такой, что файл может быть удален, пока он еще не mmap'd)?

Java NIO библиотеки mmap-файлов в Windows таким образом, что сопоставленный файл не может быть удален, пока в куче не осталось ни одной ссылки MappedByteBuffer без сбора мусора. Команда JDK утверждает, что это ограничение Windows, но только когда файлы mmap'd, а не когда они открываются как обычные файлы:

https://mail.openjdk.java.net/pipermail/nio-dev/2019-January/005698.html

(Очевидно, что если файл удален во время mmap'd, то, что именно должно произойти с областью mmap'd, является спорным в мире семантики файлов Windows, хотя в Linux это четко определено.)

Для справки, невозможность удалить файлы, пока они отображены в памяти (или еще не очищены), создает много проблем в Java:

http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/

И существуют причины безопасности, по которым операция unmap не поддерживается:

https://bugs.openjdk.java.net/browse/JDK-4724038

ОБНОВЛЕНИЕ: См. Также: Как отменить отображение файла mmap с помощью замены на пустые страницы

1 Ответ

0 голосов
/ 11 января 2019

как отмечено @ eryksun мы можем удалить сопоставленный файл, если раздел (сопоставление файлов) был создан без атрибута SEC_IMAGE двумя способами:

  • открыть файл с флагом FILE_FLAG_DELETE_ON_CLOSE - Файл быть удаленным сразу после того, как все его дескрипторы закрыты, что включает указанный дескриптор и любой другой открытый или дублированный ручки или мы можем использовать NtOpenFile или NtCreateFile звонок с флагом FILE_DELETE_ON_CLOSE.
  • по телефону ZwDeleteFile. действительно внутренний NtDeleteFile открытый файл с флагом FILE_DELETE_ON_CLOSE и специальным внутренним распоряжение DeleteOnly = TRUE. это сделать вызов больше Эффективно сравнить нормальный открытый файл и затем закрыть его дескриптор.

в коде это выглядит так.

#ifndef FILE_SHARE_VALID_FLAGS
#define FILE_SHARE_VALID_FLAGS 0x00000007
#endif

NTSTATUS Delete1(PCWSTR FileName)
{
    HANDLE hFile = CreateFile(FileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        return RtlGetLastNtStatus();
    }
    CloseHandle(hFile);
    return 0;
}

NTSTATUS Delete2(PCWSTR FileName)
{
    UNICODE_STRING ObjectName;

    if (RtlDosPathNameToNtPathName_U(FileName, &ObjectName, 0, 0))
    {
        OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };

        NTSTATUS status = ZwDeleteFile(&oa);

        RtlFreeUnicodeString(&ObjectName);

        return status;
    }

    return STATUS_UNSUCCESSFUL;
}

обратите внимание, что вызов DeleteFileW завершается с ошибкой со статусом - STATUS_CANNOT_DELETE. я рекомендую позвонить RtlGetLastNtStatus() здесь вместо GetLastError(), потому что сопоставление win32 NTSTATUS с кодом ошибки не является инъективным и часто теряет ценную информацию. скажем STATUS_CANNOT_DELETE сопоставлено с ERROR_ACCESS_DENIED. но существуют огромные другие NTSATUS коды, которые также сопоставлены с ERROR_ACCESS_DENIED. ERROR_ACCESS_DENIED не только STATUS_ACCESS_DENIED (реальный доступ запрещен). получил STATUS_CANNOT_DELETE гораздо более информативно здесь сравнить ERROR_ACCESS_DENIED. RtlGetLastNtStatus имеет точно такую ​​же подпись, что и GetLastError и экспортируется из ntdll.dll (поэтому включите ntdll.lib или ntdllp.lib )

extern "C" NTSYSCALLAPI NTSTATUS NTAPI RtlGetLastNtStatus();
...