Search code examples
cfile-mapping

Why doesn't this file-mapped variable get flushed to the disk?


I recently realized that my C skills got a bit rusty and decided to play a bit. I just ended up with a weird behavior though - the file-mapped memory I requested doesn't seem to flush the variable ifs.free_space. The result is that if you don't comment out the two //root lines, the program always starts with ifs.free_space equal to zero. However, commenting it out leads to the counter being saved. What's happening here?

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

#define FILE_LENGTH 0x10000


void* offset_to_pointer(uint64_t offset, void* file_memory) {
    return (void *)((uint64_t)file_memory + offset);
}

uint64_t pointer_to_offset(void* pointer, void* file_memory) {
    return ((uint64_t)pointer - (uint64_t)file_memory);
}

void* allocate(uint64_t size, uint64_t* free_space, void* file_memory) {
    void* ret = offset_to_pointer(*free_space, file_memory);
    *free_space += size;
    return ret;
}

typedef struct dirent {
    uint64_t prev_offset;
    uint64_t next_offset;
    uint64_t size;
} dirent;

typedef struct idiotfs {
    void* file_memory;
    uint64_t* free_space;
    dirent* root;
} idiotfs;

int main (int argc, char* const argv[])
{
    int fd = open("file.bin", O_RDWR | O_DSYNC, S_IRUSR | S_IWUSR);
    if (fd == -1)
        return 2;
    idiotfs ifs;
    ifs.file_memory = mmap(0, FILE_LENGTH,
                           PROT_READ | PROT_WRITE,
                           MAP_SHARED, fd, 0);
    ifs.free_space = (uint64_t *)offset_to_pointer(0, ifs.file_memory);
    dirent* root = (dirent *)allocate(sizeof(struct dirent) + strlen("hi") + 1,
                                      ifs.free_space, ifs.file_memory);
    //root = (dirent *)allocate(sizeof(struct dirent) + strlen("hi") + 1,
    //                          ifs.free_space, ifs.file_memory);
    root->prev_offset = 0;
    root->next_offset = 0;
    root->size = 3;
    char* text = (char *)((uint64_t)root + sizeof(dirent));
    strcpy(text, "hi");
    close(fd);
}

Solution

  • Overlapping structs

    The location of the ifs struct overlaps the location of the first root struct. This is because you started free_space at 0, so the first root struct will be allocated at offset 0 exactly where the ifs struct is at.

    Before you allocate the first root struct, you should set free_space to sizeof(ifs).