Luke Hutchison
Luke Hutchison

Reputation: 9220

Memory-mapping a file in Windows with SHARE attribute (so file is not locked against deletion)

Is there any way to map a file's content into memory in Windows that does not hold a lock on the file (in particular, such that the file can be deleted while still mmap'd)?

The Java NIO libraries mmap files in Windows in such a way that the mapped file cannot be deleted while there is any non garbage collected MappedByteBuffer reference left in the heap. The JDK team claim that this is a limitation of Windows, but only when files are mmap'd, not when they are opened as regular files:

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

(Obviously if a file is deleted while mmap'd, exactly what should happen to the mmap'd region is debatable in the world of Windows file semantics, though it's well-defined in Linux.)

For reference, the inability to delete files while they are memory mapped (or not yet garbage collected) creates a lot of problems in Java:

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

And there are security reasons why an unmap operation is not supported:

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

UPDATE: See also: How to unmap an mmap'd file by replacing with a mapping to empty pages

Upvotes: 3

Views: 916

Answers (1)

RbMm
RbMm

Reputation: 33734

as noted @eryksun we can delete mapped file, if section(file mapping) was created without SEC_IMAGE attribute in 2 ways:

in code this look like.

#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;
}

note that call DeleteFileW fail here with status - STATUS_CANNOT_DELETE. i recomendate call RtlGetLastNtStatus() here instead GetLastError() because win32 mapping NTSTATUS to error code is not injective and frequently lost valuable information. say STATUS_CANNOT_DELETE mapped to ERROR_ACCESS_DENIED. but exist huge another NTSATUS codes which also mapped to ERROR_ACCESS_DENIED. the ERROR_ACCESS_DENIED not only STATUS_ACCESS_DENIED (real access denied). got STATUS_CANNOT_DELETE much more informative here compare ERROR_ACCESS_DENIED. the RtlGetLastNtStatus have exactly same signature as GetLastError and exported from ntdll.dll ( so include ntdll.lib or ntdllp.lib)

extern "C" NTSYSCALLAPI NTSTATUS NTAPI RtlGetLastNtStatus();

Upvotes: 2

Related Questions