memsnap 0.1.2

A library for creating memory snapshots and views with copy-on-write semantics.
Documentation
use std::ops::Range;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::ptr::null_mut;

use libc::{
    MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, MAP_SHARED, PROT_EXEC, PROT_NONE, PROT_READ,
    PROT_WRITE,
};

pub type OwnedFileDescriptor = OwnedFd;
pub type RawFileDescriptor = RawFd;

use super::{effective_size, Access, Snapshot, View, ViewMode};

impl Snapshot {
    pub(super) fn from_file_impl(file: std::fs::File) -> std::io::Result<Self> {
        let size = file.metadata()?.len() as usize;
        let size = size.next_multiple_of(page_size::get());
        let file = file.into();

        Ok(Self { file, size })
    }

    pub(super) fn zeroed_impl(size: usize) -> std::io::Result<Self> {
        let size = size.next_multiple_of(page_size::get());
        let fd = unsafe { libc::memfd_create(c"hyperlight_snapshot".as_ptr() as _, 0) };
        if fd < 0 {
            return Err(std::io::Error::last_os_error());
        }
        let file = unsafe { std::fs::File::from_raw_fd(fd) };
        file.set_len(size as u64)?;
        let file = file.into();

        Ok(Self { file, size })
    }

    pub(super) fn as_raw_fd(&self) -> RawFd {
        self.file.as_raw_fd()
    }
}

impl<S> View<S> {
    pub(super) fn new(
        snapshot: S,
        fd: RawFd,
        size: usize,
        mode: ViewMode,
    ) -> std::io::Result<Self> {
        let ptr = unsafe {
            libc::mmap(
                null_mut(),
                effective_size(size),
                PROT_READ | PROT_WRITE,
                mode.as_posix() | MAP_NORESERVE,
                fd,
                0,
            )
        };
        if ptr == MAP_FAILED {
            return Err(std::io::Error::last_os_error());
        }

        let ptr = ptr as *mut u8;

        Ok(Self {
            fd,
            ptr,
            size,
            mode,
            _snapshot: snapshot,
        })
    }
}

impl<S> View<S> {
    pub(super) fn restore_impl(&mut self) -> std::io::Result<()> {
        let new_ptr = unsafe {
            libc::mmap(
                self.ptr as _,
                effective_size(self.size),
                PROT_READ | PROT_WRITE,
                self.mode.as_posix() | MAP_NORESERVE | MAP_FIXED,
                self.fd,
                0,
            )
        };
        if new_ptr == MAP_FAILED {
            return Err(std::io::Error::last_os_error());
        }
        Ok(())
    }

    pub(super) fn protect_impl(
        &mut self,
        offset: Range<usize>,
        allow: Access,
    ) -> std::io::Result<()> {
        let res = unsafe {
            libc::mprotect(
                self.ptr.add(offset.start) as _,
                offset.len(),
                allow.as_posix(),
            )
        };
        if res < 0 {
            return Err(std::io::Error::last_os_error());
        }
        Ok(())
    }
}

impl<S> Drop for View<S> {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(self.ptr as _, self.size);
        }
    }
}

impl Access {
    fn as_posix(&self) -> libc::c_int {
        let mut access = 0;
        if *self == Access::NONE {
            access = PROT_NONE;
        } else {
            if self.contains(Access::READ) {
                access |= PROT_READ;
            }
            if self.contains(Access::WRITE) {
                access |= PROT_WRITE | PROT_READ;
            }
            if self.contains(Access::EXEC) {
                access |= PROT_EXEC;
            }
        }
        access
    }
}

impl ViewMode {
    fn as_posix(&self) -> libc::c_int {
        match self {
            ViewMode::Cow => MAP_PRIVATE,
            ViewMode::Mutable => MAP_SHARED,
        }
    }
}