use crate::{Error, Result};
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::errno::Errno;
#[cfg(not(target_os = "android"))]
use crate::fcntl::OFlag;
use libc::{self, c_int, c_void, size_t, off_t};
#[cfg(not(target_os = "android"))]
use crate::sys::stat::Mode;
use std::os::unix::io::RawFd;
libc_bitflags!{
/// Desired memory protection of a memory mapping.
pub struct ProtFlags: c_int {
/// Pages cannot be accessed.
PROT_NONE;
/// Pages can be read.
PROT_READ;
/// Pages can be written.
PROT_WRITE;
/// Pages can be executed
PROT_EXEC;
/// Apply protection up to the end of a mapping that grows upwards.
#[cfg(any(target_os = "android", target_os = "linux"))]
PROT_GROWSDOWN;
/// Apply protection down to the beginning of a mapping that grows downwards.
#[cfg(any(target_os = "android", target_os = "linux"))]
PROT_GROWSUP;
}
}
libc_bitflags!{
/// Additional parameters for `mmap()`.
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
MAP_FILE;
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
MAP_SHARED;
/// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
MAP_PRIVATE;
/// Place the mapping at exactly the address specified in `addr`.
MAP_FIXED;
/// Synonym for `MAP_ANONYMOUS`.
MAP_ANON;
/// The mapping is not backed by any file.
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
MAP_ANONYMOUS;
/// Put the mapping into the first 2GB of the process address space.
#[cfg(any(all(any(target_os = "android", target_os = "linux"),
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 = "freebsd", target_pointer_width = "64")))]
MAP_32BIT;
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_GROWSDOWN;
/// Compatibility flag. Ignored.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_DENYWRITE;
/// Compatibility flag. Ignored.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_EXECUTABLE;
/// Mark the mmaped region to be locked in the same way as `mlock(2)`.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_LOCKED;
/// Do not reserve swap space for this mapping.
///
/// This was removed in FreeBSD 11.
#[cfg(not(target_os = "freebsd"))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_POPULATE;
/// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_NONBLOCK;
/// Allocate the mapping using "huge pages."
#[cfg(any(target_os = "android", target_os = "linux"))]
MAP_HUGETLB;
/// Make use of 64KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_64KB;
/// Make use of 512KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512KB;
/// Make use of 1MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1MB;
/// Make use of 2MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2MB;
/// Make use of 8MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_8MB;
/// Make use of 16MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16MB;
/// Make use of 32MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_32MB;
/// Make use of 256MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_256MB;
/// Make use of 512MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512MB;
/// Make use of 1GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1GB;
/// Make use of 2GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2GB;
/// Make use of 16GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16GB;
/// Lock the mapped region into memory as with `mlock(2)`.
#[cfg(target_os = "netbsd")]
MAP_WIRED;
/// Causes dirtied data in the specified range to be flushed to disk only when necessary.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
MAP_NOSYNC;
/// Rename private pages to a file.
///
/// This was removed in FreeBSD 11.
#[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
MAP_RENAME;
/// Region may contain semaphores.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
MAP_STACK;
/// Pages in this mapping are not retained in the kernel's memory cache.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MAP_NOCACHE;
#[cfg(any(target_os = "ios", target_os = "macos"))]
MAP_JIT;
}
}
#[cfg(target_os = "linux")]
libc_bitflags!{
/// Options for `mremap()`.
pub struct MRemapFlags: c_int {
/// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
MREMAP_MAYMOVE;
/// Place the mapping at exactly the address specified in `new_address`.
MREMAP_FIXED;
}
}
libc_enum!{
/// Usage information for a range of memory to allow for performance optimizations by the kernel.
///
/// Used by [`madvise`](./fn.madvise.html).
#[repr(i32)]
pub enum MmapAdvise {
/// No further special treatment. This is the default.
MADV_NORMAL,
/// Expect random page references.
MADV_RANDOM,
/// Expect sequential page references.
MADV_SEQUENTIAL,
/// Expect access in the near future.
MADV_WILLNEED,
/// Do not expect access in the near future.
MADV_DONTNEED,
/// Free up a given range of pages and its associated backing store.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_REMOVE,
/// Do not make pages in this range available to the child after a `fork(2)`.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_DONTFORK,
/// Undo the effect of `MADV_DONTFORK`.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_DOFORK,
/// Poison the given pages.
///
/// Subsequent references to those pages are treated like hardware memory corruption.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_HWPOISON,
/// Enable Kernel Samepage Merging (KSM) for the given pages.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_MERGEABLE,
/// Undo the effect of `MADV_MERGEABLE`
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_UNMERGEABLE,
/// Preserve the memory of each page but offline the original page.
#[cfg(any(target_os = "android",
all(target_os = "linux", any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "ppc",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "sparc64"))))]
MADV_SOFT_OFFLINE,
/// Enable Transparent Huge Pages (THP) for pages in the given range.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_HUGEPAGE,
/// Undo the effect of `MADV_HUGEPAGE`.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_NOHUGEPAGE,
/// Exclude the given range from a core dump.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_DONTDUMP,
/// Undo the effect of an earlier `MADV_DONTDUMP`.
#[cfg(any(target_os = "android", target_os = "linux"))]
MADV_DODUMP,
/// Specify that the application no longer needs the pages in the given range.
MADV_FREE,
/// Request that the system not flush the current range to disk unless it needs to.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
MADV_NOSYNC,
/// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
MADV_AUTOSYNC,
/// Region is not included in a core file.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
MADV_NOCORE,
/// Include region in a core file
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
MADV_CORE,
#[cfg(any(target_os = "freebsd"))]
MADV_PROTECT,
/// Invalidate the hardware page table for the given region.
#[cfg(target_os = "dragonfly")]
MADV_INVAL,
/// Set the offset of the page directory page to `value` for the virtual page table.
#[cfg(target_os = "dragonfly")]
MADV_SETMAP,
/// Indicates that the application will not need the data in the given range.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MADV_ZERO_WIRED_PAGES,
#[cfg(any(target_os = "ios", target_os = "macos"))]
MADV_FREE_REUSABLE,
#[cfg(any(target_os = "ios", target_os = "macos"))]
MADV_FREE_REUSE,
#[cfg(any(target_os = "ios", target_os = "macos"))]
MADV_CAN_REUSE,
}
}
libc_bitflags!{
/// Configuration flags for `msync`.
pub struct MsFlags: c_int {
/// Schedule an update but return immediately.
MS_ASYNC;
/// Invalidate all cached data.
MS_INVALIDATE;
/// Invalidate pages, but leave them mapped.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MS_KILLPAGES;
/// Deactivate pages, but leave them mapped.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MS_DEACTIVATE;
/// Perform an update and wait for it to complete.
MS_SYNC;
}
}
libc_bitflags!{
/// Flags for `mlockall`.
pub struct MlockAllFlags: c_int {
/// Lock pages that are currently mapped into the address space of the process.
MCL_CURRENT;
/// Lock pages which will become mapped into the address space of the process in the future.
MCL_FUTURE;
}
}
/// Locks all memory pages that contain part of the address range with `length`
/// bytes starting at `addr`.
///
/// Locked pages never move to the swap area.
///
/// # Safety
///
/// `addr` must meet all the requirements described in the `mlock(2)` man page.
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::mlock(addr, length)).map(drop)
}
/// Unlocks all memory pages that contain part of the address range with
/// `length` bytes starting at `addr`.
///
/// # Safety
///
/// `addr` must meet all the requirements described in the `munlock(2)` man
/// page.
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::munlock(addr, length)).map(drop)
}
/// Locks all memory pages mapped into this process' address space.
///
/// Locked pages never move to the swap area.
///
/// # Safety
///
/// `addr` must meet all the requirements described in the `mlockall(2)` man
/// page.
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}
/// Unlocks all memory pages mapped into this process' address space.
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
/// allocate memory, or map files or devices into memory
///
/// # Safety
///
/// See the `mmap(2)` man page for detailed requirements.
pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
if ret == libc::MAP_FAILED {
Err(Error::Sys(Errno::last()))
} else {
Ok(ret)
}
}
/// Expands (or shrinks) an existing memory mapping, potentially moving it at
/// the same time.
///
/// # Safety
///
/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
/// detailed requirements.
#[cfg(target_os = "linux")]
pub unsafe fn mremap(
addr: *mut c_void,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<* mut c_void>,
) -> Result<*mut c_void> {
let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));
if ret == libc::MAP_FAILED {
Err(Error::Sys(Errno::last()))
} else {
Ok(ret)
}
}
/// remove a mapping
///
/// # Safety
///
/// `addr` must meet all the requirements described in the `munmap(2)` man
/// page.
pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
Errno::result(libc::munmap(addr, len)).map(drop)
}
/// give advice about use of memory
///
/// # Safety
///
/// See the `madvise(2)` man page. Take special care when using
/// `MmapAdvise::MADV_FREE`.
pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
}
/// Set protection of memory mapping.
///
/// See [`mprotect(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
/// details.
///
/// # Safety
///
/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
/// SIGSEGVs.
///
/// ```
/// # use nix::libc::size_t;
/// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
/// # use std::ptr;
/// const ONE_K: size_t = 1024;
/// let mut slice: &mut [u8] = unsafe {
/// let mem = mmap(ptr::null_mut(), ONE_K, ProtFlags::PROT_NONE,
/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
/// };
/// assert_eq!(slice[0], 0x00);
/// slice[0] = 0xFF;
/// assert_eq!(slice[0], 0xFF);
/// ```
pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()> {
Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
}
/// synchronize a mapped region
///
/// # Safety
///
/// `addr` must meet all the requirements described in the `msync(2)` man
/// page.
pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
}
#[cfg(not(target_os = "android"))]
pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
let ret = name.with_nix_path(|cstr| {
#[cfg(any(target_os = "macos", target_os = "ios"))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
})?;
Errno::result(ret)
}
#[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)
}