blob: 61c4f8a2060dcc8bb03ca0656c97f16818752645 [file] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use serde::ser;
use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use super::RawDescriptor;
use super::Result;
use crate::descriptor::AsRawDescriptor;
use crate::descriptor::IntoRawDescriptor;
use crate::descriptor::SafeDescriptor;
use crate::MemoryMapping;
/// A shared memory file descriptor and its size.
#[derive(Debug, Deserialize)]
#[serde(try_from = "SerializedSharedMemory")]
pub struct SharedMemory {
pub descriptor: SafeDescriptor,
pub size: u64,
// Elements used internally to perform File-like operations on this Shared Memory
pub mapping: MemoryMapping,
pub cursor: usize,
}
impl SharedMemory {
/// Gets the size in bytes of the shared memory.
///
/// The size returned here does not reflect changes by other interfaces or users of the shared
/// memory file descriptor.
pub fn size(&self) -> u64 {
self.size
}
}
// Ideally we'd use Serde's "into" attribute on SharedMemory to convert into SerializedSharedMemory
// prior to serialization; however, this requires SharedMemory to implement Clone, which does not
// make sense for all its fields.
impl Serialize for SharedMemory {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = SerializedSharedMemory {
descriptor: self.descriptor.try_clone().map_err(|e| {
ser::Error::custom(format!(
"Error cloning SharedMemory::descriptor while serializing SharedMemory: {}",
e
))
})?,
size: self.size,
};
s.serialize(serializer)
}
}
/// Serialization helper for SharedMemory.
///
/// SharedMemory::mapping cannot be serialized because when sent across processes. This is because
/// the memory region it refers may change. To solve that, we serialize SharedMemory as
/// SerializedSharedMemory instead, and on deserialization, Serde uses TryFrom to create a
/// SharedMemory, which creates a brand new MemoryMapping (in SharedMemory::mapping) from the
/// descriptor.
#[derive(Serialize, Deserialize)]
struct SerializedSharedMemory {
#[serde(with = "crate::with_as_descriptor")]
pub descriptor: SafeDescriptor,
pub size: u64,
}
impl TryFrom<SerializedSharedMemory> for SharedMemory {
type Error = crate::Error;
fn try_from(shm: SerializedSharedMemory) -> Result<Self> {
SharedMemory::from_safe_descriptor(
shm.descriptor,
#[cfg(windows)]
Some(shm.size),
)
}
}
/// USE THIS CAUTIOUSLY. The returned handle is not a file handle and cannot be
/// used as if it were one. It is a handle to a the associated file mapping object
/// and should only be used for memory-mapping the file view.
impl AsRawDescriptor for SharedMemory {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.descriptor.as_raw_descriptor()
}
}
impl IntoRawDescriptor for SharedMemory {
fn into_raw_descriptor(self) -> RawDescriptor {
self.descriptor.into_raw_descriptor()
}
}
impl Read for SharedMemory {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let result = match self.mapping.read_slice(buf, self.cursor) {
Ok(result) => result,
Err(e) => {
return Err(Error::new(
ErrorKind::Other,
format!("Unable to read from shared memory: {}", e),
));
}
};
let size_read = result;
self.cursor += size_read;
Ok(size_read)
}
}
impl Write for SharedMemory {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let result = match self.mapping.write_slice(buf, self.cursor) {
Ok(result) => result,
Err(e) => {
return Err(Error::new(
ErrorKind::Other,
format!("Unable to write to shared memory: {}", e),
));
}
};
let size_written = result;
self.cursor += size_written;
Ok(size_written)
}
fn flush(&mut self) -> io::Result<()> {
// No buffering is used, no flushing required
Ok(())
}
}
impl Seek for SharedMemory {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let new_cursor: i64 = match pos {
SeekFrom::Start(offset) => offset as i64,
SeekFrom::End(offset) => self.size as i64 + offset,
SeekFrom::Current(offset) => self.cursor as i64 + offset,
};
if new_cursor < 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
"Cannot seek to a negative value",
));
}
self.cursor = new_cursor as usize;
Ok(self.cursor as u64)
}
}
#[cfg(test)]
mod tests {
use std::ffi::CString;
use super::*;
#[test]
fn new() {
let shm = SharedMemory::new(&CString::new("name").unwrap(), 1028)
.expect("failed to create shared memory");
assert_eq!(shm.size(), 1028);
}
}