FILE_FLAG_DELETE_ON_CLOSE and memory mapped files

天涯浪子 提交于 2019-12-31 03:59:30

问题


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 deleted? In which case, if the page is swapped out the data will be lost?

#include <iostream>
#include <memory>
#include <windows.h>

int main()
{
    typedef std::unique_ptr<void, decltype(&CloseHandle)> Handle;
    typedef std::unique_ptr<void, decltype(&UnmapViewOfFile)> View;

    View v(nullptr, UnmapViewOfFile);

    {
        Handle h(CreateFile(
            L"test",
            GENERIC_READ | GENERIC_WRITE,
            0,
            nullptr,
            CREATE_ALWAYS,
            FILE_FLAG_DELETE_ON_CLOSE,
            nullptr
        ), CloseHandle);

        // write something so CreateFileMapping succeeds
        DWORD sz;
        WriteFile(h.get(), "hello world", 12, &sz, nullptr);

        Handle m(CreateFileMapping(
            h.get(),
            nullptr,
            PAGE_READWRITE,
            0, 0,
            nullptr
        ), CloseHandle);

        v.reset(MapViewOfFile(
            m.get(),
            FILE_MAP_WRITE,
            0, 0,
            0
        ));

        char c;
        std::cin >> c; // File is still in folder
    }

    char c;
    std::cin >> c; // No file!

    std::cout << static_cast<char*>(v.get()); // Still writes
}

回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/11099295/file-flag-delete-on-close-and-memory-mapped-files

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!