use crate::errno::Errno;
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::Result;
#[cfg(not(target_os = "android"))]
#[cfg(feature = "fs")]
use crate::{fcntl::OFlag, sys::stat::Mode};
use libc::{self, c_int, c_void, off_t, size_t};
use std::ptr::NonNull;
use std::{
num::NonZeroUsize,
os::unix::io::{AsFd, AsRawFd},
};
libc_bitflags! {
pub struct ProtFlags: c_int {
PROT_NONE;
PROT_READ;
PROT_WRITE;
PROT_EXEC;
#[cfg(linux_android)]
PROT_GROWSDOWN;
#[cfg(linux_android)]
PROT_GROWSUP;
}
}
libc_bitflags! {
pub struct MapFlags: c_int {
#[cfg(not(any(target_os = "solaris", target_os = "redox")))]
MAP_FILE;
MAP_SHARED;
#[cfg(target_os = "linux")]
MAP_SHARED_VALIDATE;
MAP_PRIVATE;
MAP_FIXED;
#[cfg(target_os = "linux")]
MAP_FIXED_NOREPLACE;
#[cfg(target_os = "freebsd")]
MAP_EXCL;
MAP_ANON;
MAP_ANONYMOUS;
#[cfg(any(all(linux_android,
any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "ohos", target_arch = "x86_64"),
all(target_os = "freebsd", target_pointer_width = "64")))]
MAP_32BIT;
#[cfg(linux_android)]
MAP_GROWSDOWN;
#[cfg(linux_android)]
MAP_DENYWRITE;
#[cfg(linux_android)]
MAP_EXECUTABLE;
#[cfg(linux_android)]
MAP_LOCKED;
#[cfg(not(any(freebsdlike, target_os = "aix", target_os = "hurd", target_os = "redox")))]
MAP_NORESERVE;
#[cfg(linux_android)]
MAP_POPULATE;
#[cfg(linux_android)]
MAP_NONBLOCK;
#[cfg(linux_android)]
MAP_HUGETLB;
#[cfg(target_os = "linux")]
MAP_HUGE_64KB;
#[cfg(target_os = "linux")]
MAP_HUGE_512KB;
#[cfg(target_os = "linux")]
MAP_HUGE_1MB;
#[cfg(target_os = "linux")]
MAP_HUGE_2MB;
#[cfg(target_os = "linux")]
MAP_HUGE_8MB;
#[cfg(target_os = "linux")]
MAP_HUGE_16MB;
#[cfg(target_os = "linux")]
MAP_HUGE_32MB;
#[cfg(target_os = "linux")]
MAP_HUGE_256MB;
#[cfg(target_os = "linux")]
MAP_HUGE_512MB;
#[cfg(target_os = "linux")]
MAP_HUGE_1GB;
#[cfg(target_os = "linux")]
MAP_HUGE_2GB;
#[cfg(target_os = "linux")]
MAP_HUGE_16GB;
#[cfg(target_os = "netbsd")]
MAP_WIRED;
#[cfg(freebsdlike)]
MAP_NOSYNC;
#[cfg(netbsdlike)]
MAP_RENAME;
#[cfg(any(freebsdlike, netbsdlike))]
MAP_HASSEMAPHORE;
#[cfg(any(linux_android, freebsdlike, netbsdlike))]
MAP_STACK;
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")), not(target_env = "uclibc")))]
MAP_SYNC;
#[cfg(apple_targets)]
MAP_NOCACHE;
#[cfg(apple_targets)]
MAP_JIT;
#[cfg(target_os = "freebsd")]
MAP_ALIGNED_SUPER;
#[cfg(target_os = "openbsd")]
MAP_CONCEAL;
#[cfg(netbsdlike)]
MAP_TRYFIXED;
}
}
impl MapFlags {
#[cfg(any(linux_android, target_os = "fuchsia"))]
pub fn map_hugetlb_with_size_log2(
huge_page_size_log2: u32,
) -> Option<Self> {
if (16..=63).contains(&huge_page_size_log2) {
let flag = libc::MAP_HUGETLB
| (huge_page_size_log2 << libc::MAP_HUGE_SHIFT) as i32;
Some(Self(flag.into()))
} else {
None
}
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
libc_bitflags! {
pub struct MRemapFlags: c_int {
#[cfg(target_os = "linux")]
MREMAP_MAYMOVE;
#[cfg(target_os = "linux")]
MREMAP_FIXED;
#[cfg(target_os = "linux")]
MREMAP_DONTUNMAP;
#[cfg(target_os = "netbsd")]
MAP_FIXED;
#[cfg(target_os = "netbsd")]
MAP_REMAPDUP;
}
}
libc_enum! {
#[repr(i32)]
#[non_exhaustive]
pub enum MmapAdvise {
MADV_NORMAL,
MADV_RANDOM,
MADV_SEQUENTIAL,
MADV_WILLNEED,
MADV_DONTNEED,
#[cfg(linux_android)]
MADV_REMOVE,
#[cfg(linux_android)]
MADV_DONTFORK,
#[cfg(linux_android)]
MADV_DOFORK,
#[cfg(linux_android)]
MADV_HWPOISON,
#[cfg(linux_android)]
MADV_MERGEABLE,
#[cfg(linux_android)]
MADV_UNMERGEABLE,
#[cfg(any(target_os = "android",
all(target_os = "linux", any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "sparc64"))))]
MADV_SOFT_OFFLINE,
#[cfg(linux_android)]
MADV_HUGEPAGE,
#[cfg(linux_android)]
MADV_NOHUGEPAGE,
#[cfg(linux_android)]
MADV_DONTDUMP,
#[cfg(linux_android)]
MADV_DODUMP,
#[cfg(not(any(target_os = "aix", target_os = "hurd", target_os = "cygwin", target_os = "redox")))]
MADV_FREE,
#[cfg(freebsdlike)]
MADV_NOSYNC,
#[cfg(freebsdlike)]
MADV_AUTOSYNC,
#[cfg(freebsdlike)]
MADV_NOCORE,
#[cfg(freebsdlike)]
MADV_CORE,
#[cfg(any(target_os = "freebsd"))]
MADV_PROTECT,
#[cfg(target_os = "dragonfly")]
MADV_INVAL,
#[cfg(target_os = "dragonfly")]
MADV_SETMAP,
#[cfg(apple_targets)]
MADV_ZERO_WIRED_PAGES,
#[cfg(apple_targets)]
MADV_FREE_REUSABLE,
#[cfg(apple_targets)]
MADV_FREE_REUSE,
#[cfg(apple_targets)]
#[allow(missing_docs)]
MADV_CAN_REUSE,
#[cfg(linux_android)]
MADV_PAGEOUT,
#[cfg(linux_android)]
MADV_COLD,
#[cfg(linux_android)]
MADV_WIPEONFORK,
#[cfg(linux_android)]
MADV_KEEPONFORK,
#[cfg(linux_android)]
MADV_POPULATE_READ,
#[cfg(linux_android)]
MADV_POPULATE_WRITE,
}
}
libc_bitflags! {
pub struct MsFlags: c_int {
MS_ASYNC;
MS_INVALIDATE;
#[cfg(apple_targets)]
MS_KILLPAGES;
#[cfg(apple_targets)]
MS_DEACTIVATE;
MS_SYNC;
}
}
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
libc_bitflags! {
pub struct MlockAllFlags: c_int {
MCL_CURRENT;
MCL_FUTURE;
}
}
pub unsafe fn mlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::mlock(addr.as_ptr(), length)).map(drop) }
}
pub unsafe fn munlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::munlock(addr.as_ptr(), length)).map(drop) }
}
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
pub unsafe fn mmap<F: AsFd>(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
f: F,
offset: off_t,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let fd = f.as_fd().as_raw_fd();
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
pub unsafe fn mmap_anonymous(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let flags = MapFlags::MAP_ANONYMOUS | flags;
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
pub unsafe fn mremap(
addr: NonNull<c_void>,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<NonNull<c_void>>,
) -> Result<NonNull<c_void>> {
#[cfg(target_os = "linux")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_size,
flags.bits(),
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
)
};
#[cfg(target_os = "netbsd")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
new_size,
flags.bits(),
)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
pub unsafe fn munmap(addr: NonNull<c_void>, len: size_t) -> Result<()> {
unsafe { Errno::result(libc::munmap(addr.as_ptr(), len)).map(drop) }
}
#[allow(rustdoc::broken_intra_doc_links)] pub unsafe fn madvise(
addr: NonNull<c_void>,
length: size_t,
advise: MmapAdvise,
) -> Result<()> {
let ptr = {
#[cfg(target_os = "aix")]
{
addr.as_ptr() as *mut u8
}
#[cfg(not(target_os = "aix"))]
{
addr.as_ptr()
}
};
unsafe {
Errno::result(libc::madvise(ptr, length, advise as i32)).map(drop)
}
}
pub unsafe fn mprotect(
addr: NonNull<c_void>,
length: size_t,
prot: ProtFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::mprotect(addr.as_ptr(), length, prot.bits()))
.map(drop)
}
}
pub unsafe fn msync(
addr: NonNull<c_void>,
length: size_t,
flags: MsFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::msync(addr.as_ptr(), length, flags.bits()))
.map(drop)
}
}
#[cfg(not(target_os = "android"))]
feature! {
#![feature = "fs"]
pub fn shm_open<P>(
name: &P,
flag: OFlag,
mode: Mode
) -> Result<std::os::unix::io::OwnedFd>
where P: ?Sized + NixPath
{
use std::os::unix::io::{FromRawFd, OwnedFd};
let ret = name.with_nix_path(|cstr| {
#[cfg(apple_targets)]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
}
#[cfg(not(apple_targets))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
})?;
match ret {
-1 => Err(Errno::last()),
fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) })
}
}
}
#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
let ret =
name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
Errno::result(ret).map(drop)
}