summaryrefslogtreecommitdiffstats
path: root/third_party/rust/audioipc2/src/shm.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/audioipc2/src/shm.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/audioipc2/src/shm.rs')
-rw-r--r--third_party/rust/audioipc2/src/shm.rs334
1 files changed, 334 insertions, 0 deletions
diff --git a/third_party/rust/audioipc2/src/shm.rs b/third_party/rust/audioipc2/src/shm.rs
new file mode 100644
index 0000000000..2aaa92ef36
--- /dev/null
+++ b/third_party/rust/audioipc2/src/shm.rs
@@ -0,0 +1,334 @@
+// Copyright © 2017 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+#![allow(clippy::missing_safety_doc)]
+
+use crate::errors::*;
+use crate::PlatformHandle;
+use std::{convert::TryInto, ffi::c_void, slice};
+
+#[cfg(unix)]
+pub use unix::SharedMem;
+#[cfg(windows)]
+pub use windows::SharedMem;
+
+#[derive(Copy, Clone)]
+struct SharedMemView {
+ ptr: *mut c_void,
+ size: usize,
+}
+
+unsafe impl Send for SharedMemView {}
+
+impl SharedMemView {
+ pub unsafe fn get_slice(&self, size: usize) -> Result<&[u8]> {
+ let map = slice::from_raw_parts(self.ptr as _, self.size);
+ if size <= self.size {
+ Ok(&map[..size])
+ } else {
+ bail!("mmap size");
+ }
+ }
+
+ pub unsafe fn get_mut_slice(&mut self, size: usize) -> Result<&mut [u8]> {
+ let map = slice::from_raw_parts_mut(self.ptr as _, self.size);
+ if size <= self.size {
+ Ok(&mut map[..size])
+ } else {
+ bail!("mmap size")
+ }
+ }
+}
+
+#[cfg(unix)]
+mod unix {
+ use super::*;
+ use memmap2::{MmapMut, MmapOptions};
+ use std::fs::File;
+ use std::os::unix::io::{AsRawFd, FromRawFd};
+
+ #[cfg(target_os = "android")]
+ fn open_shm_file(_id: &str, size: usize) -> Result<File> {
+ unsafe {
+ let fd = ashmem::ASharedMemory_create(std::ptr::null(), size);
+ if fd >= 0 {
+ // Drop PROT_EXEC
+ let r = ashmem::ASharedMemory_setProt(fd, libc::PROT_READ | libc::PROT_WRITE);
+ assert_eq!(r, 0);
+ return Ok(File::from_raw_fd(fd.try_into().unwrap()));
+ }
+ Err(std::io::Error::last_os_error().into())
+ }
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn open_shm_file(id: &str, size: usize) -> Result<File> {
+ let file = open_shm_file_impl(id)?;
+ allocate_file(&file, size)?;
+ Ok(file)
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn open_shm_file_impl(id: &str) -> Result<File> {
+ use std::env::temp_dir;
+ use std::fs::{remove_file, OpenOptions};
+
+ let id_cstring = std::ffi::CString::new(id).unwrap();
+
+ #[cfg(target_os = "linux")]
+ {
+ unsafe {
+ let r = libc::syscall(libc::SYS_memfd_create, id_cstring.as_ptr(), 0);
+ if r >= 0 {
+ return Ok(File::from_raw_fd(r.try_into().unwrap()));
+ }
+ }
+
+ let mut path = std::path::PathBuf::from("/dev/shm");
+ path.push(id);
+
+ if let Ok(file) = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create_new(true)
+ .open(&path)
+ {
+ let _ = remove_file(&path);
+ return Ok(file);
+ }
+ }
+
+ unsafe {
+ let fd = libc::shm_open(
+ id_cstring.as_ptr(),
+ libc::O_RDWR | libc::O_CREAT | libc::O_EXCL,
+ 0o600,
+ );
+ if fd >= 0 {
+ libc::shm_unlink(id_cstring.as_ptr());
+ return Ok(File::from_raw_fd(fd));
+ }
+ }
+
+ let mut path = temp_dir();
+ path.push(id);
+
+ let file = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create_new(true)
+ .open(&path)?;
+
+ let _ = remove_file(&path);
+ Ok(file)
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn handle_enospc(s: &str) -> Result<()> {
+ let err = std::io::Error::last_os_error();
+ let errno = err.raw_os_error().unwrap_or(0);
+ assert_ne!(errno, 0);
+ debug!("allocate_file: {} failed errno={}", s, errno);
+ if errno == libc::ENOSPC {
+ return Err(err.into());
+ }
+ Ok(())
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn allocate_file(file: &File, size: usize) -> Result<()> {
+ // First, set the file size. This may create a sparse file on
+ // many systems, which can fail with SIGBUS when accessed via a
+ // mapping and the lazy backing allocation fails due to low disk
+ // space. To avoid this, try to force the entire file to be
+ // preallocated before mapping using OS-specific approaches below.
+
+ file.set_len(size.try_into().unwrap())?;
+
+ let fd = file.as_raw_fd();
+ let size: libc::off_t = size.try_into().unwrap();
+
+ // Try Linux-specific fallocate.
+ #[cfg(target_os = "linux")]
+ {
+ if unsafe { libc::fallocate(fd, 0, 0, size) } == 0 {
+ return Ok(());
+ }
+ handle_enospc("fallocate()")?;
+ }
+
+ // Try macOS-specific fcntl.
+ #[cfg(target_os = "macos")]
+ {
+ let params = libc::fstore_t {
+ fst_flags: libc::F_ALLOCATEALL,
+ fst_posmode: libc::F_PEOFPOSMODE,
+ fst_offset: 0,
+ fst_length: size,
+ fst_bytesalloc: 0,
+ };
+ if unsafe { libc::fcntl(fd, libc::F_PREALLOCATE, &params) } == 0 {
+ return Ok(());
+ }
+ handle_enospc("fcntl(F_PREALLOCATE)")?;
+ }
+
+ // Fall back to portable version, where available.
+ #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))]
+ {
+ if unsafe { libc::posix_fallocate(fd, 0, size) } == 0 {
+ return Ok(());
+ }
+ handle_enospc("posix_fallocate()")?;
+ }
+
+ Ok(())
+ }
+
+ pub struct SharedMem {
+ file: File,
+ _mmap: MmapMut,
+ view: SharedMemView,
+ }
+
+ impl SharedMem {
+ pub fn new(id: &str, size: usize) -> Result<SharedMem> {
+ let file = open_shm_file(id, size)?;
+ let mut mmap = unsafe { MmapOptions::new().len(size).map_mut(&file)? };
+ assert_eq!(mmap.len(), size);
+ let view = SharedMemView {
+ ptr: mmap.as_mut_ptr() as _,
+ size,
+ };
+ Ok(SharedMem {
+ file,
+ _mmap: mmap,
+ view,
+ })
+ }
+
+ pub unsafe fn make_handle(&self) -> Result<PlatformHandle> {
+ PlatformHandle::duplicate(self.file.as_raw_fd()).map_err(|e| e.into())
+ }
+
+ pub unsafe fn from(handle: PlatformHandle, size: usize) -> Result<SharedMem> {
+ let file = File::from_raw_fd(handle.into_raw());
+ let mut mmap = MmapOptions::new().len(size).map_mut(&file)?;
+ assert_eq!(mmap.len(), size);
+ let view = SharedMemView {
+ ptr: mmap.as_mut_ptr() as _,
+ size,
+ };
+ Ok(SharedMem {
+ file,
+ _mmap: mmap,
+ view,
+ })
+ }
+
+ pub unsafe fn get_slice(&self, size: usize) -> Result<&[u8]> {
+ self.view.get_slice(size)
+ }
+
+ pub unsafe fn get_mut_slice(&mut self, size: usize) -> Result<&mut [u8]> {
+ self.view.get_mut_slice(size)
+ }
+
+ pub fn get_size(&self) -> usize {
+ self.view.size
+ }
+ }
+}
+
+#[cfg(windows)]
+mod windows {
+ use super::*;
+ use std::ptr;
+ use winapi::{
+ shared::{minwindef::DWORD, ntdef::HANDLE},
+ um::{
+ handleapi::CloseHandle,
+ memoryapi::{MapViewOfFile, UnmapViewOfFile, FILE_MAP_ALL_ACCESS},
+ winbase::CreateFileMappingA,
+ winnt::PAGE_READWRITE,
+ },
+ };
+
+ use crate::INVALID_HANDLE_VALUE;
+
+ pub struct SharedMem {
+ handle: HANDLE,
+ view: SharedMemView,
+ }
+
+ unsafe impl Send for SharedMem {}
+
+ impl Drop for SharedMem {
+ fn drop(&mut self) {
+ unsafe {
+ let ok = UnmapViewOfFile(self.view.ptr);
+ assert_ne!(ok, 0);
+ let ok = CloseHandle(self.handle);
+ assert_ne!(ok, 0);
+ }
+ }
+ }
+
+ impl SharedMem {
+ pub fn new(_id: &str, size: usize) -> Result<SharedMem> {
+ unsafe {
+ let handle = CreateFileMappingA(
+ INVALID_HANDLE_VALUE,
+ ptr::null_mut(),
+ PAGE_READWRITE,
+ (size as u64 >> 32).try_into().unwrap(),
+ (size as u64 & (DWORD::MAX as u64)).try_into().unwrap(),
+ ptr::null(),
+ );
+ if handle.is_null() {
+ return Err(std::io::Error::last_os_error().into());
+ }
+
+ let ptr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ if ptr.is_null() {
+ return Err(std::io::Error::last_os_error().into());
+ }
+
+ Ok(SharedMem {
+ handle,
+ view: SharedMemView { ptr, size },
+ })
+ }
+ }
+
+ pub unsafe fn make_handle(&self) -> Result<PlatformHandle> {
+ PlatformHandle::duplicate(self.handle).map_err(|e| e.into())
+ }
+
+ pub unsafe fn from(handle: PlatformHandle, size: usize) -> Result<SharedMem> {
+ let handle = handle.into_raw();
+ let ptr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ if ptr.is_null() {
+ return Err(std::io::Error::last_os_error().into());
+ }
+ Ok(SharedMem {
+ handle,
+ view: SharedMemView { ptr, size },
+ })
+ }
+
+ pub unsafe fn get_slice(&self, size: usize) -> Result<&[u8]> {
+ self.view.get_slice(size)
+ }
+
+ pub unsafe fn get_mut_slice(&mut self, size: usize) -> Result<&mut [u8]> {
+ self.view.get_mut_slice(size)
+ }
+
+ pub fn get_size(&self) -> usize {
+ self.view.size
+ }
+ }
+}