extern crate byteorder;
use std::collections::HashMap;
use std::io::{BufReader, Read};
use std::fs::File;
use std::path::Path;
use std::marker::PhantomData;
use std;
use self::byteorder::{ByteOrder, ReadBytesExt, NativeEndian};
use super::{AuxvPair, AuxvType};
pub fn search_procfs_auxv(keys: &[AuxvType])
-> Result<HashMap<AuxvType, AuxvType>, ProcfsAuxvError> {
let mut result = HashMap::<AuxvType, AuxvType>::new();
for r in iterate_path::<NativeEndian>(&Path::new("/proc/self/auxv"))? {
let pair = match r {
Ok(p) => p,
Err(e) => return Err(e)
};
if keys.contains(&pair.key) {
let _ = result.insert(pair.key, pair.value);
}
}
return Ok(result);
}
pub fn iterate_procfs_auxv() -> Result<ProcfsAuxvIter<NativeEndian, File>, ProcfsAuxvError> {
iterate_path::<NativeEndian>(&Path::new("/proc/self/auxv"))
}
#[derive(Debug, PartialEq)]
pub enum ProcfsAuxvError {
IoError,
InvalidFormat
}
pub struct ProcfsAuxvIter<B: ByteOrder, R: Read> {
pair_size: usize,
buf: Vec<u8>,
input: BufReader<R>,
keep_going: bool,
phantom_byteorder: PhantomData<B>
}
fn iterate_path<B: ByteOrder>(path: &Path)
-> Result<ProcfsAuxvIter<B, File>, ProcfsAuxvError> {
let input = File::open(path)
.map_err(|_| ProcfsAuxvError::IoError)
.map(|f| BufReader::new(f))?;
let pair_size = 2 * std::mem::size_of::<AuxvType>();
let buf: Vec<u8> = Vec::with_capacity(pair_size);
Ok(ProcfsAuxvIter::<B, File> {
pair_size: pair_size,
buf: buf,
input: input,
keep_going: true,
phantom_byteorder: PhantomData
})
}
impl<B: ByteOrder, R: Read> Iterator for ProcfsAuxvIter<B, R> {
type Item = Result<AuxvPair, ProcfsAuxvError>;
fn next(&mut self) -> Option<Self::Item> {
if !self.keep_going {
return None
}
self.keep_going = false;
self.buf.clear();
for _ in 0 .. self.pair_size {
self.buf.push(0);
}
let mut read_bytes: usize = 0;
while read_bytes < self.pair_size {
match self.input.read(&mut self.buf[read_bytes..]) {
Ok(n) => {
if n == 0 {
return Some(Err(ProcfsAuxvError::InvalidFormat))
}
read_bytes += n;
}
Err(_) => return Some(Err(ProcfsAuxvError::IoError))
}
}
let mut reader = &self.buf[..];
let aux_key = match read_long::<B>(&mut reader) {
Ok(x) => x,
Err(_) => return Some(Err(ProcfsAuxvError::InvalidFormat))
};
let aux_val = match read_long::<B>(&mut reader) {
Ok(x) => x,
Err(_) => return Some(Err(ProcfsAuxvError::InvalidFormat))
};
if aux_key == 0 {
return None;
}
self.keep_going = true;
Some(Ok(AuxvPair {
key: aux_key,
value: aux_val
}))
}
}
fn read_long<B: ByteOrder> (reader: &mut Read) -> std::io::Result<AuxvType>{
match std::mem::size_of::<AuxvType>() {
4 => reader.read_u32::<B>().map(|u| u as AuxvType),
8 => reader.read_u64::<B>().map(|u| u as AuxvType),
x => panic!("Unexpected type width: {}", x)
}
}
#[cfg(test)]
mod tests {
use std::path::Path;
use super::iterate_path;
#[cfg(target_pointer_width="64")]
use super::ProcfsAuxvError;
use super::byteorder::*;
use super::super::AuxvPair;
#[test]
#[cfg(target_pointer_width="64")]
fn test_iterate_auxv_path_real_linux_x64() {
let path = Path::new("src/test-data/linux-x64-i7-6850k.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
assert_eq!(AuxvPair { key: 33, value: 140724395515904 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 16, value: 3219913727 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 6, value: 4096 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 17, value: 100 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 3, value: 4194368 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 4, value: 56 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 5, value: 10 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 7, value: 139881368498176 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 8, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 9, value: 4204128 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 11, value: 1000 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 12, value: 1000 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 13, value: 1000 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 14, value: 1000 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 23, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 25, value: 140724393842889 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 31, value: 140724393852911 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 15, value: 140724393842905 }, iter.next().unwrap().unwrap());
assert_eq!(None, iter.next());
}
#[test]
#[cfg(target_pointer_width="32")]
fn test_iterate_auxv_path_virtualbox_linux_x86() {
let path = Path::new("src/test-data/macos-virtualbox-linux-x86-4850HQ.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
assert_eq!(AuxvPair { key: 32, value: 3078061308 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 33, value: 3078057984 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 16, value: 126614527 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 6, value: 4096}, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 17, value: 100}, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 3, value: 134512692 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 4, value: 32 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 5, value: 9 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 7, value: 3078066176 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 8, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 9, value: 134520424 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 11, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 12, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 13, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 14, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 23, value: 0 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 25, value: 3219671659 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 31, value: 3219677171 }, iter.next().unwrap().unwrap());
assert_eq!(AuxvPair { key: 15, value: 3219671675 }, iter.next().unwrap().unwrap());
assert_eq!(None, iter.next());
}
#[test]
#[cfg(target_pointer_width="64")]
fn test_iterate_auxv_path_real_linux_no_value_in_trailing_null() {
let path = Path::new("src/test-data/linux-x64-i7-6850k-mangled-no-value-in-trailing-null.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
assert_eq!(AuxvPair { key: 33, value: 140724395515904 }, iter.next().unwrap().unwrap());
let mut skipped = iter.skip(16);
assert_eq!(AuxvPair { key: 15, value: 140724393842905 }, skipped.next().unwrap().unwrap());
assert_eq!(ProcfsAuxvError::InvalidFormat, skipped.next().unwrap().unwrap_err());
assert_eq!(None, skipped.next());
}
#[test]
#[cfg(target_pointer_width="64")]
fn test_iterate_auxv_path_real_linux_truncated_entry() {
let path = Path::new("src/test-data/linux-x64-i7-6850k-mangled-truncated-entry.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
assert_eq!(AuxvPair { key: 33, value: 140724395515904 }, iter.next().unwrap().unwrap());
let mut skipped = iter.skip(15);
assert_eq!(AuxvPair { key: 31, value: 140724393852911 }, skipped.next().unwrap().unwrap());
assert_eq!(ProcfsAuxvError::InvalidFormat, skipped.next().unwrap().unwrap_err());
assert_eq!(None, skipped.next());
}
#[test]
#[cfg(target_pointer_width="64")]
fn test_iterate_auxv_path_real_linux_no_trailing_null() {
let path = Path::new("src/test-data/linux-x64-i7-6850k-mangled-no-trailing-null.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
assert_eq!(AuxvPair { key: 33, value: 140724395515904 }, iter.next().unwrap().unwrap());
let mut skipped = iter.skip(16);
assert_eq!(AuxvPair { key: 15, value: 140724393842905 }, skipped.next().unwrap().unwrap());
assert_eq!(ProcfsAuxvError::InvalidFormat, skipped.next().unwrap().unwrap_err());
assert_eq!(None, skipped.next());
}
#[test]
#[cfg(target_pointer_width="64")]
fn test_parse_auxv_path_virtualbox_linux_32bit_in_64bit_mode_invalidformat() {
let path = Path::new("src/test-data/macos-virtualbox-linux-x86-4850HQ.auxv");
let mut iter = iterate_path::<LittleEndian>(path).unwrap();
for _ in 0..10 {
assert!(iter.next().unwrap().is_ok());
}
assert_eq!(ProcfsAuxvError::InvalidFormat, iter.next().unwrap().unwrap_err());
}
}