FILE_FLAG_DELETE_ON_CLOSE and memory mapped files

前端 未结 2 1230
时光取名叫无心
时光取名叫无心 2021-01-15 21:16

Not that it\'s particularly useful, but I\'m curious as to why the following works, is it simply because the page still happens to be in memory even after the file is delete

相关标签:
2条回答
  • 2021-01-15 21:51

    Windows is different from Linux and other Unix-alikes in this regard. It maintains a separate reference and handle count for each FILE_OBJECT. When the last handle is closed, the filesystem unlinks the file and switches it to a "deleted" state. Meanwhile the FILE_OBJECT might live longer in a "deleted" state until reference count drops to zero.

    Quote from here:

    The file gets deleted when you close the file handle. After this, if any pages from the view are trimmed and repurposed and then accessed again, the memory manager will try to read them from the file (which is now in the "deleted" state). What happens next depends on the filesystem. NTFS returns an error code (STATUS_END_OF_FILE) which causes the memory manager to satisfy the page fault with zeroed pages. As far as I can tell, this behavior is not documented so a future version of NTFS (or a different filesystem) could return a different error, which would result in an inpage exception instead.

    That means that once the file is unlinked, swapped-out data will be lost and replaced with zeroes.

    You can observe this behavior with the program below. It does the following:

    • Creates a file of a size equal to the amount of RAM with FILE_FLAG_DELETE_ON_CLOSE.
    • Maps it into memory.
    • Deletes the file by closing the last handle to it.
    • Fills file content with 0xCD pattern. Since the file is as large as RAM, this should force pages out of a memory and into a file.
    • Prints first 16 bytes of the mapping.

    The output says the data at the beginning of the mapping is all zeroes - data has been lost. It is not clear if this can be considered a bug, but as of today this behavior is at least 12 years old and is not likely to be changed.

    #include <stdio.h>
    #include <stdint.h>
    
    #include <Windows.h>
    
    int main()
    {
        LARGE_INTEGER size;
        MEMORYSTATUSEX mem = { 0 };
        mem.dwLength = sizeof mem;
        GlobalMemoryStatusEx(&mem);
        size.QuadPart = mem.ullTotalPhys;
    
        const HANDLE hFile = CreateFileW(
            L"file-under-test",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
            NULL);
    
        if (hFile == INVALID_HANDLE_VALUE) {
            fprintf(stderr, "CreateFileW() failed\n");
            return 1;
        }
    
        if (!SetFilePointerEx(hFile, size, NULL, SEEK_SET)) {
            fprintf(stderr, "SetFilePointerEx() failed\n");
            return 1;
        }
    
        if (!SetEndOfFile(hFile)) {
            fprintf(stderr, "SetEndOfFile() failed\n");
            return 1;
        }
    
        const HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    
        if (hFile == INVALID_HANDLE_VALUE) {
            fprintf(stderr, "CreateFileMappingW() failed\n");
            return 1;
        }
    
        void *const view = MapViewOfFile(
            hMap,
            FILE_MAP_READ | FILE_MAP_WRITE,
            0, 0,
            (size_t) size.QuadPart);
    
        if (view == NULL) {
            fprintf(stderr, "MapViewOfFile() failed\n");
            return 1;
        }
    
        CloseHandle(hMap);
        CloseHandle(hFile); // this removes the file
        memset(view, 0xCD, (size_t) size.QuadPart);
    
        // Print first 16 bytes
        for (int i = 0; i < 16; ++i) {
            printf("%2d: %#x\n", i, ((const volatile unsigned char *) view)[i]);
        }
    
        return 0;
    }
    
    0 讨论(0)
  • 2021-01-15 21:54

    FILE_FLAG_DELETE_ON_CLOSE follows the unfortunate Windows tradition of referring to an unlink operation as "delete". In fact, the flag only causes the file to be unlinked from the specified directory when the file is closed.

    Like other operating systems, Windows only gives ordinary user code the ability to unlink a file from a particular directory. Deleting is always the operating system's decision, taking place when the file can no longer be referenced in any way.

    If you look, you'll see the file has in fact been unlinked from the directory, but it will not actually be deleted (and the space the data takes on disk available for re-use) until its reference count drops to zero. The mapping holds a reference.

    0 讨论(0)
提交回复
热议问题