How to create and write to memory mapped files?

The real answer is to use a crate that provides this functionality, ideally in a cross-platform manner.

use memmap; // 0.7.0
use std::{
    fs::OpenOptions,
    io::{Seek, SeekFrom, Write},
};

const SIZE: u64 = 1024 * 1024;

fn main() {
    let src = "https://stackoverflow.com/questions/28516996/Hello!";

    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("test.mmap")
        .expect("Unable to open file");

    // Allocate space in the file first
    f.seek(SeekFrom::Start(SIZE)).unwrap();
    f.write_all(&[0]).unwrap();
    f.seek(SeekFrom::Start(0)).unwrap();

    let mut data = unsafe {
        memmap::MmapOptions::new()
            .map_mut(&f)
            .expect("Could not access data from memory mapped file")
    };

    data[..src.len()].copy_from_slice(src.as_bytes());
}

Note that this is still possible for this code to lead to undefined behavior. Since the slice is backed by a file, the contents of the file (and thus the slice) may change from outside of the Rust program, breaking the invariants that the unsafe block is supposed to hold. The programmer needs to ensure that the file doesn’t change during the lifetime of the map. Unfortunately, the crate itself does not provide much assistance to prevent this from happening or even any documentation warning the user.


If you wish to use lower-level system calls, you are missing two main parts:

  1. mmap doesn’t allocate any space on its own, so you need to set some space in the file. Without this, I get Illegal instruction: 4 when running on macOS.

  2. MemoryMap (was) private by default so you need to mark the mapping as public so that changes are written back to the file (I’m assuming you want the writes to be saved). Without this, the code runs, but the file is never changed.

Here’s a version that works for me:

use libc; // 0.2.67
use std::{
    fs::OpenOptions,
    io::{Seek, SeekFrom, Write},
    os::unix::prelude::AsRawFd,
    ptr,
};

fn main() {
    let src = "https://stackoverflow.com/questions/28516996/Hello!";

    let size = 1024 * 1024;

    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("test.mmap")
        .expect("Unable to open file");

    // Allocate space in the file first
    f.seek(SeekFrom::Start(size as u64)).unwrap();
    f.write_all(&[0]).unwrap();
    f.seek(SeekFrom::Start(0)).unwrap();

    // This refers to the `File` but doesn't use lifetimes to indicate
    // that. This is very dangerous, and you need to be careful.
    unsafe {
        let data = libc::mmap(
            /* addr: */ ptr::null_mut(),
            /* len: */ size,
            /* prot: */ libc::PROT_READ | libc::PROT_WRITE,
            // Then make the mapping *public* so it is written back to the file
            /* flags: */ libc::MAP_SHARED,
            /* fd: */ f.as_raw_fd(),
            /* offset: */ 0,
        );

        if data == libc::MAP_FAILED {
            panic!("Could not access data from memory mapped file")
        }

        ptr::copy_nonoverlapping(src.as_ptr(), data as *mut u8, src.len());
    }
}

Leave a Comment