sys_util: add sys_util crate for misc system modules

This initial commit includes an mmap wrapper.

TEST=cargo test
BUG=None

Change-Id: I9625bd446fcd4801b2e16188897e84714b4e4ce0
Reviewed-on: https://chromium-review.googlesource.com/496987
Commit-Ready: Dylan Reid <dgreid@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Zach Reizner 2017-05-03 14:06:12 -07:00 committed by chrome-bot
parent 303f86fa21
commit 00f90a4bba
4 changed files with 196 additions and 0 deletions

8
sys_util/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "sys_util"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
libc = "*"

38
sys_util/src/errno.rs Normal file
View file

@ -0,0 +1,38 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::result;
use libc::__errno_location;
/// An error number, retrieved from [errno](http://man7.org/linux/man-pages/man3/errno.3.html), set
/// by a libc function that returned an error.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Error(i32);
pub type Result<T> = result::Result<T, Error>;
impl Error {
/// Constructs a new error with the given errno.
pub fn new(e: i32) -> Error {
Error(e)
}
/// Constructs an error from the current errno.
///
/// The result of this only has any meaning just after a libc call that returned a value
/// indicating errno was set.
pub fn last() -> Error {
Error(unsafe { *__errno_location() })
}
/// Gets the errno for this error
pub fn errno(&self) -> i32 {
self.0
}
}
/// Returns the last errno as a Result that is always an error.
pub fn errno_result<T>() -> Result<T> {
Err(Error::last())
}

14
sys_util/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Small system utility modules for usage by other modules.
extern crate libc;
mod mmap;
mod errno;
pub use mmap::*;
pub use errno::{Error, Result};
use errno::errno_result;

136
sys_util/src/mmap.rs Normal file
View file

@ -0,0 +1,136 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The mmap module provides a safe interface to mmap memory and ensures unmap is called when the
//! mmap object leaves scope.
use std;
use std::ptr::null_mut;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use libc;
use {Result, errno_result};
/// Wraps an anonymous shared memory mapping in the current process.
pub struct MemoryMapping {
addr: *mut u8,
size: usize,
ref_count: Arc<AtomicUsize>,
}
unsafe impl Send for MemoryMapping {}
impl MemoryMapping {
/// Creates an anonymous shared mapping of `size` bytes.
pub fn new(size: usize) -> Result<MemoryMapping> {
// This is safe because we are creating an anonymous mapping in a place not already used by
// any other area in this process.
let addr = unsafe {
libc::mmap(null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
-1,
0)
};
if addr == null_mut() {
return errno_result();
}
Ok(MemoryMapping {
addr: addr as *mut u8,
size: size,
ref_count: Arc::new(AtomicUsize::new(1)),
})
}
/// Maps the first `size` bytes of the given `fd`.
pub fn from_fd(fd: &AsRawFd, size: usize) -> Result<MemoryMapping> {
// This is safe because we are creating a mapping in a place not already used by any other
// area in this process.
let addr = unsafe {
libc::mmap(null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd.as_raw_fd(),
0)
};
if addr == null_mut() {
return errno_result();
}
Ok(MemoryMapping {
addr: addr as *mut u8,
size: size,
ref_count: Arc::new(AtomicUsize::new(1)),
})
}
pub fn as_ptr(&self) -> *mut u8 {
self.addr
}
pub fn size(&self) -> usize {
self.size
}
#[deprecated(note="use volatile_read with the ptr instead")]
pub fn as_slice(&self) -> &[u8] {
// This is safe because we mapped the area at addr ourselves, so this slice will not
// overflow. However, it is possible to alias, hence the deprecation.
unsafe { std::slice::from_raw_parts(self.addr, self.size) }
}
#[deprecated(note="use volatile_write with the ptr instead")]
pub fn as_mut_slice(&self) -> &mut [u8] {
// This is safe because we mapped the area at addr ourselves, so this slice will not
// overflow. However, it is possible to alias, hence the deprecation.
unsafe { std::slice::from_raw_parts_mut(self.addr, self.size) }
}
// TODO(zachr): remove when we no longer need it, clone is sketchy
pub fn clone(&self) -> MemoryMapping {
self.ref_count.fetch_add(1, Ordering::SeqCst);
MemoryMapping {
addr: self.addr,
size: self.size,
ref_count: self.ref_count.clone(),
}
}
}
impl Drop for MemoryMapping {
fn drop(&mut self) {
if self.ref_count.fetch_sub(1, Ordering::SeqCst) == 1 {
// This is safe because we mmap the area at addr ourselves, and the ref_count ensures
// nobody else is holding a reference to it.
unsafe {
libc::munmap(self.addr as *mut libc::c_void, self.size);
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn basic_map() {
let m = MemoryMapping::new(1024).unwrap();
assert_eq!(1024, m.size());
}
#[test]
fn mutate_slices() {
let m = MemoryMapping::new(1024).unwrap();
assert_eq!(1024, m.size());
{
m.as_mut_slice()[128] = 55;
}
assert_eq!(m.as_slice()[128], 55);
}
}