Add virtio block test util

Change-Id: Icb0fe5d324fa56542cbdad84f70b6e507ffd377d
diff --git a/Cargo.lock b/Cargo.lock
index e12886a..6598979 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,4 +1,179 @@
 [[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clap"
+version = "2.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.43"
+
+[[package]]
+name = "libc"
+version = "0.2.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "linux_tests"
 version = "0.1.0"
+dependencies = [
+ "libc 0.2.43",
+ "structopt 0.2.12",
+]
 
+[[package]]
+name = "proc-macro2"
+version = "0.4.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_termios"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "redox_syscall 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "strsim"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "structopt"
+version = "0.2.12"
+dependencies = [
+ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt-derive 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.15.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termion"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vec_map"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.6"
+
+[[patch.unused]]
+name = "winapi-build"
+version = "0.3.6"
+
+[[patch.unused]]
+name = "winapi-util"
+version = "0.1.1"
+
+[metadata]
+"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
+"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
+"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
+"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
+"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
+"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
+"checksum redox_syscall 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "cf8fb82a4d1c9b28f1c26c574a5b541f5ffb4315f6c9a791fa47b6a04438fe93"
+"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
+"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+"checksum structopt-derive 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "17ff01fe96de9d16e7372ae5f19dd7ece2c703b51043c3db9ea27f9e393ea311"
+"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
+"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
+"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
+"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
diff --git a/Cargo.toml b/Cargo.toml
index 003dfd4..d401652 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,4 +11,11 @@
 license = "BSD-3-Clause"
 edition = "2018"
 
-[dependencies]
\ No newline at end of file
+[dependencies]
+structopt = { path = "rust-crates/rustc_deps/vendor/structopt" }
+libc = { path = "rust-crates/rustc_deps/vendor/libc" }
+
+[patch.crates-io]
+winapi = { path = "rust-crates/rustc_deps/tiny_mirrors/winapi" }
+winapi-build = { path = "rust-crates/rustc_deps/tiny_mirrors/winapi-build" }
+winapi-util = { path = "rust-crates/rustc_deps/tiny_mirrors/winapi-util" }
diff --git a/build.sh b/build.sh
index 87e4eae..3557af4 100755
--- a/build.sh
+++ b/build.sh
@@ -37,6 +37,7 @@
 
 mkdir -p ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_rng_test_util ${OUT_DIR}
+cp target/${TARGET}/debug/virtio_block_test_util ${OUT_DIR}
 
 declare -r BLOCK_SIZE=4096
 declare -r ADDITIONAL_BLOCKS=1024
diff --git a/src/bin/virtio_block_test_util.rs b/src/bin/virtio_block_test_util.rs
new file mode 100644
index 0000000..59d6964
--- /dev/null
+++ b/src/bin/virtio_block_test_util.rs
@@ -0,0 +1,138 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#![deny(warnings)]
+
+use libc::{self, c_int};
+use std::fs::{self, File, OpenOptions};
+use std::io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
+use std::os::unix::io::AsRawFd;
+use std::path::Path;
+use structopt::StructOpt;
+
+const DEV_DIR: &str = "/dev";
+
+// Ioctl requests are comprised of 32 bits:
+// [31:30]  Access mode
+// [29:16]  Size of the parameter structure
+// [15:8]   Request type
+// [7:0]    Request number
+// For the ioctls we care about here there are no parameters, so we set only a type and a number.
+// See Linux:include/uapi/asm-generic/ioctl.h for more details.
+const TYPE_SHIFT: usize = 8;
+macro_rules! define_ioctl {
+    ($name:ident, $typ:expr, $num:expr, $return_type:ty) => {
+        fn $name(file: &File) -> $return_type {
+            let request: c_int = ($typ << TYPE_SHIFT) | ($num & 0xff);
+            let mut r: $return_type = 0;
+            unsafe {
+                libc::ioctl(file.as_raw_fd(), request, &mut r);
+            }
+            r
+        }
+    };
+}
+
+// Block ioctl types and number are defined in Linux:include/uapi/linux/fs.h.
+define_ioctl!(block_dev_size, 0x12, 96, u64);
+define_ioctl!(block_dev_sector_size, 0x12, 104, u32);
+
+#[derive(StructOpt, Debug)]
+struct Config {
+    block_size: u32,
+    block_count: u64,
+    #[structopt(subcommand)]
+    cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+    #[structopt(name = "check")]
+    Check,
+    #[structopt(name = "read")]
+    Read { offset: u64, expected: u8 },
+    #[structopt(name = "write")]
+    Write { offset: u64, value: u8 },
+}
+
+fn find_block_device(block_size: u32, block_count: u64, write: bool) -> std::io::Result<File> {
+    let dir = Path::new(DEV_DIR);
+    if !dir.is_dir() {
+        return Err(Error::new(
+            ErrorKind::Other,
+            format!("{} is not a directory", DEV_DIR),
+        ));
+    }
+    for entry in fs::read_dir(dir)?.collect::<std::io::Result<Vec<_>>>()? {
+        if !entry
+            .file_name()
+            .to_str()
+            .map_or(false, |f| f.starts_with("vd"))
+        {
+            continue;
+        }
+        let file = OpenOptions::new()
+            .read(true)
+            .write(write)
+            .open(entry.path())?;
+        if block_size != block_dev_sector_size(&file) {
+            continue;
+        }
+        if block_count != block_dev_size(&file) {
+            continue;
+        }
+        return Ok(file);
+    }
+    Err(Error::new(ErrorKind::NotFound, "Block device not found"))
+}
+
+fn read_block(
+    block_dev: &mut File,
+    block_size: u32,
+    offset: u64,
+    expected: u8,
+) -> std::io::Result<()> {
+    block_dev.seek(SeekFrom::Start(offset * block_size as u64))?;
+    let mut data: Vec<u8> = vec![0; block_size as usize];
+    block_dev.read_exact(&mut data)?;
+    if !data.iter().all(|&b| b == expected) {
+        return Err(Error::new(ErrorKind::Other, "Incorrect data read"));
+    }
+    Ok(())
+}
+
+fn write_block(
+    block_dev: &mut File,
+    block_size: u32,
+    offset: u64,
+    value: u8,
+) -> std::io::Result<()> {
+    block_dev.seek(SeekFrom::Start(offset * block_size as u64))?;
+    let data: Vec<u8> = vec![value; block_size as usize];
+    block_dev.write_all(&data)?;
+    block_dev.sync_all()?;
+    Ok(())
+}
+
+fn main() -> std::io::Result<()> {
+    let config = Config::from_args();
+    let write = if let Command::Write { .. } = config.cmd {
+        true
+    } else {
+        false
+    };
+    let mut block_dev = find_block_device(config.block_size, config.block_count, write)?;
+    let result = match config.cmd {
+        Command::Check => Ok(()),
+        Command::Read { offset, expected } => {
+            read_block(&mut block_dev, config.block_size, offset, expected)
+        }
+        Command::Write { offset, value } => {
+            write_block(&mut block_dev, config.block_size, offset, value)
+        }
+    };
+    if result.is_ok() {
+        println!("PASS");
+    }
+    result
+}