Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mmap() after deleting the file

Tags:

c++

I was told, that mmap() might be in trouble, if someone deletes the original file. I was wondering if that really happens. So i created some little test-program. I am using linux.

#include <iostream>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main(int, char**)
{
    char const * const fileName = "/tmp/demo-file.dat";
    size_t size;

    {
        struct stat st;
        stat(fileName, &st);
        size = st.st_size;
    }

    int fd = open(fileName, O_RDWR);
    if (fd == -1)
    {
        std::cout << "open() failed, errno = " << errno << ":" << strerror(errno) << std::endl;
        return (-1);
    }
    else
    {
        std::cout << "open() done (ok)" << std::endl;
    }

    for (int i = 20; i > 0; --i)
    {
        std::cout << "file open()'ed, wait #" << i << " seconds before mmap()" << std::endl;
        sleep(1);
    }

    void *data = mmap((void*)0L, size, PROT_READ, MAP_SHARED, fd, (off_t)0);
    if (data == (void*)-1)
    {
        std::cout << "mmap() failed, errno = " << errno << ":" << strerror(errno) << std::endl;
    }
    else
    {
        std::cout << "mmap() done (ok)" << std::endl;
    }

    for (int i = 20; i > 0; --i)
    {
        std::cout << "going to close() socket in #" << i << " seconds" << std::endl;
        sleep (1);
    }

    close(fd);

    for (int i = 30; i > 0; --i)
    {
        std::cout << "going to umap() files in #" << i << " seconds (still accessing the data)" << std::endl;
        for (unsigned int x = 0; x < size; ++x)
        {
            char cp = *(char*) (data + x);
            (void) cp;
        }
        sleep(1);
    }

    munmap(data, size);

    for (int i = 30; i > 0; --i)
    {
        std::cout << "going to terminate #" << i << " seconds" << std::endl;
        sleep(1);
    }

    return 0;
}

Whenever i delete the file - after the open() operation - it doesn't have negative impact to then mmap(). I can still acess the data in the test program. When i delete the file right after close(), but before mmap(), it works. I can also still see the old file in the /proc/[pid]/fd/ area:

lrwx------ 1 frank frank 64 Mär 22 20:28 3 -> /tmp/demo-file.dat (deleted)

The rest of the program works.

Even when i delete the file after the close() it still succeeds to access the mmap() data. However in both cases, after the close() i cannot see the

lrwx------ 1 frank frank 64 Mär 22 20:28 3 -> /tmp/demo-file.dat (deleted)

anymore. (btw: where is then noted, that this file "still exists somehow"?)

So is it the opposite, that it is guaranteed, that mmap() will still be able to operate on the data, even if the file was manually deleted (in a shell or by some other process)?

like image 279
Frank Bergemann Avatar asked Nov 09 '22 00:11

Frank Bergemann


1 Answers

Here's what's happening.

The first thing to check is

$ls -i /tmp/demo-file.dat
65 /tmp/demo-file.dat

Note the inode number of the file is 65.

On starting the program, here's what it has in its lsof output (apart from other entries not relevant to the current discourse)

a.out   29271 zoso    3u   REG   0,21        5       65 /tmp/demo-file.dat

This is a result of the open() that's been done. Note that the inode number is the same as the other file. The open() has increased the ref count on the same inode. Also, note that REG indicates a regular file.

Now if the file is deleted (using rm etc.), here's what the lsof looks like

a.out   29271 zoso    3u   REG   0,21        5       65 /tmp/demo-file.dat (deleted)

This is expected since the file that was opened has been deleted but the handle that to its inode still is open in the process.

Moving on to the mmap, and here's the lsof output

a.out   29271 zoso  DEL    REG   0,21                65 /tmp/demo-file.dat
a.out   29271 zoso    3u   REG   0,21        5       65 /tmp/demo-file.dat (deleted)

Now there's another new entry but note that this is of type DEL which indicates (lifting from lsof man page):

''DEL'' for a Linux map file that has been deleted;

Since lsof can't stat the original file anymore, it puts this mapping as DEL with no size of course, yet note that the inode number still remains the same i.e 65.

Now after close() has been called on the fd here's what lsof shows

a.out   29271 zoso  DEL    REG   0,21                65 /tmp/demo-file.dat

Note that the (deleted) entry is gone since that fd to a REG file has been closed and now only the mmap'd memory remains.

After munmap() this entry too is gone (no more references to /tmp/demo-file.dat and finally the program ends.

like image 162
Zoso Avatar answered Nov 15 '22 07:11

Zoso