summary refs log tree commit diff
path: root/sys_util/src/struct_util.rs
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2017-05-03 19:37:03 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-05-10 20:58:06 -0700
commit5d084600e9b2abbefe02b761f968bfc385c70723 (patch)
tree5d3f4ddd1b8bcfdd6b23e4181f16f3376c60b6b0 /sys_util/src/struct_util.rs
parent66b86b50cad0ee726194bee54c60bfbb7abb4fbe (diff)
downloadcrosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.gz
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.bz2
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.lz
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.xz
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.zst
crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.zip
sys_util: Add struct utils
These utilities are used for reading structs from u8 slices.
Often the kernel returns pointers to void pointers that represent a struct.
This helps convert back to the struct.

Change-Id: I6e59fb772f3ba75f006c9370412267ff565dfe4c
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/497768
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'sys_util/src/struct_util.rs')
-rw-r--r--sys_util/src/struct_util.rs136
1 files changed, 136 insertions, 0 deletions
diff --git a/sys_util/src/struct_util.rs b/sys_util/src/struct_util.rs
new file mode 100644
index 0000000..05625b1
--- /dev/null
+++ b/sys_util/src/struct_util.rs
@@ -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.
+
+use std;
+use std::io::Read;
+use std::mem;
+
+#[derive(Debug)]
+pub enum Error {
+    ReadStruct,
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Reads a struct from an input buffer.
+/// This is unsafe because the struct is initialized to unverified data read from the input.
+/// `read_struct` should only be called to fill plain old data structs.  It is not endian safe.
+///
+/// # Arguments
+///
+/// * `f` - The input to read from.  Often this is a file.
+/// * `out` - The struct to fill with data read from `f`.
+pub unsafe fn read_struct<T: Copy, F: Read>(f: &mut F, out: &mut T) -> Result<()> {
+    let out_slice = std::slice::from_raw_parts_mut(out as *mut T as *mut u8, mem::size_of::<T>());
+    f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
+    Ok(())
+}
+
+/// Reads an array of structs from an input buffer.  Returns a Vec of structs initialized with data
+/// from the specified input.
+/// This is unsafe because the structs are initialized to unverified data read from the input.
+/// `read_struct_slice` should only be called for plain old data structs.  It is not endian safe.
+///
+/// # Arguments
+///
+/// * `f` - The input to read from.  Often this is a file.
+/// * `len` - The number of structs to fill with data read from `f`.
+pub unsafe fn read_struct_slice<T: Copy, F: Read>(f: &mut F, len: usize) -> Result<Vec<T>> {
+    let mut out: Vec<T> = Vec::with_capacity(len);
+    out.set_len(len);
+    let out_slice = std::slice::from_raw_parts_mut(out.as_ptr() as *mut T as *mut u8,
+                                                   mem::size_of::<T>() * len);
+    f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
+    Ok(out)
+}
+
+#[cfg(test)]
+mod test {
+    use std::io::Cursor;
+    use std::mem;
+    use super::*;
+
+    #[derive(Clone, Copy, Debug, Default, PartialEq)]
+    struct TestRead {
+        a: u64,
+        b: u8,
+        c: u8,
+        d: u8,
+        e: u8,
+    }
+
+    #[test]
+    fn struct_basic_read() {
+        let orig = TestRead {
+            a: 0x7766554433221100,
+            b: 0x88,
+            c: 0x99,
+            d: 0xaa,
+            e: 0xbb,
+        };
+        let source = unsafe {
+            // Don't worry it's a test
+            std::slice::from_raw_parts(&orig as *const _ as *const u8,
+                                       std::mem::size_of::<TestRead>())
+        };
+        assert_eq!(mem::size_of::<TestRead>(), mem::size_of_val(&source));
+        let mut tr: TestRead = Default::default();
+        unsafe {
+            read_struct(&mut Cursor::new(source), &mut tr).unwrap();
+        }
+        assert_eq!(orig, tr);
+    }
+
+    #[test]
+    fn struct_read_past_end() {
+        let orig = TestRead {
+            a: 0x7766554433221100,
+            b: 0x88,
+            c: 0x99,
+            d: 0xaa,
+            e: 0xbb,
+        };
+        let source = unsafe {
+            // Don't worry it's a test
+            std::slice::from_raw_parts(&orig as *const _ as *const u8,
+                                       std::mem::size_of::<TestRead>() - 1)
+        };
+        let mut tr: TestRead = Default::default();
+        unsafe {
+            assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err());
+        }
+    }
+
+    #[test]
+    fn struct_slice_read() {
+        let orig = vec![TestRead {
+                            a: 0x7766554433221100,
+                            b: 0x88,
+                            c: 0x99,
+                            d: 0xaa,
+                            e: 0xbb,
+                        },
+                        TestRead {
+                            a: 0x7867564534231201,
+                            b: 0x02,
+                            c: 0x13,
+                            d: 0x24,
+                            e: 0x35,
+                        },
+                        TestRead {
+                            a: 0x7a69584736251403,
+                            b: 0x04,
+                            c: 0x15,
+                            d: 0x26,
+                            e: 0x37,
+                        }];
+        let source = unsafe {
+            // Don't worry it's a test
+            std::slice::from_raw_parts(orig.as_ptr() as *const u8,
+                                       std::mem::size_of::<TestRead>() * 3)
+        };
+
+        let tr: Vec<TestRead> = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() };
+        assert_eq!(orig, tr);
+    }
+}