#![allow(unsafe_code)]
use std::ffi::{c_char, c_void};
use std::io;
use std::os::fd::RawFd;
unsafe extern "C" {
fn memfd_create(name: *const c_char, flags: u32) -> i32;
fn ftruncate(fd: i32, length: i64) -> i32;
fn mmap(addr: *mut c_void, len: usize, prot: i32, flags: i32, fd: i32, off: i64)
-> *mut c_void;
fn munmap(addr: *mut c_void, len: usize) -> i32;
fn close(fd: i32) -> i32;
}
const PROT_READ: i32 = 1;
const PROT_WRITE: i32 = 2;
const MAP_SHARED: i32 = 1;
const MAP_FAILED: *mut c_void = usize::MAX as *mut c_void;
pub struct SharedBuffer {
fd: RawFd,
ptr: *mut u8,
len: usize,
}
impl SharedBuffer {
pub fn create(len: usize) -> io::Result<Self> {
if len == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"zero-length shared buffer",
));
}
let name = c"stipple-shm";
let fd = unsafe { memfd_create(name.as_ptr(), 0) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
if unsafe { ftruncate(fd, len as i64) } < 0 {
let e = io::Error::last_os_error();
unsafe { close(fd) };
return Err(e);
}
Self::map(fd, len)
}
pub fn from_fd(fd: RawFd, len: usize) -> io::Result<Self> {
Self::map(fd, len)
}
fn map(fd: RawFd, len: usize) -> io::Result<Self> {
let ptr = unsafe {
mmap(
core::ptr::null_mut(),
len,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0,
)
};
if ptr == MAP_FAILED {
let e = io::Error::last_os_error();
unsafe { close(fd) };
return Err(e);
}
Ok(Self {
fd,
ptr: ptr as *mut u8,
len,
})
}
#[inline]
pub fn fd(&self) -> RawFd {
self.fd
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
}
}
impl core::fmt::Debug for SharedBuffer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SharedBuffer")
.field("fd", &self.fd)
.field("len", &self.len)
.finish()
}
}
impl Drop for SharedBuffer {
fn drop(&mut self) {
unsafe {
munmap(self.ptr as *mut c_void, self.len);
close(self.fd);
}
}
}
unsafe impl Send for SharedBuffer {}
#[cfg(test)]
mod tests {
use super::*;
unsafe extern "C" {
fn dup(oldfd: i32) -> i32;
}
#[test]
fn second_mapping_sees_writes_through_the_shared_memfd() {
let mut a = SharedBuffer::create(64).expect("create");
a.as_mut_slice()[..4].copy_from_slice(&[1, 2, 3, 4]);
let fd2 = unsafe { dup(a.fd()) };
assert!(fd2 >= 0, "dup failed");
let mut b = SharedBuffer::from_fd(fd2, 64).expect("from_fd");
assert_eq!(&b.as_slice()[..4], &[1, 2, 3, 4]);
b.as_mut_slice()[8..12].copy_from_slice(&[9, 9, 9, 9]);
assert_eq!(&a.as_slice()[8..12], &[9, 9, 9, 9]);
}
}