vhost-device/vhost-device-scsi/src/scsi/emulation/tests/mod.rs
Manos Pitsidianakis a1e013286f Move all crates to workspace root
Having all the workspace crates under the crates/ directory is
unnecessary. Rust documentation itself recommends all crates to be in
the root directory:

https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#creating-the-second-package-in-the-workspace

I paste the text content here, in case the online page ever changes or
becomes unavailable:

    ## Creating the Second Package in the Workspace

    Next, let’s create another member package in the workspace and call it add_one. Change the top-level Cargo.toml to specify the add_one path in the members list:

    Filename: Cargo.toml

    [workspace]

    members = [
        "adder",
        "add_one",
    ]

    Then generate a new library crate named add_one:

    $ cargo new add_one --lib
         Created library `add_one` package

    Your add directory should now have these directories and files:

    ├── Cargo.lock
    ├── Cargo.toml
    ├── add_one
    │   ├── Cargo.toml
    │   └── src
    │       └── lib.rs
    ├── adder
    │   ├── Cargo.toml
    │   └── src
    │       └── main.rs
    └── target

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
2023-10-16 12:03:57 +05:30

520 lines
14 KiB
Rust

// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
#![cfg(test)]
mod bad_lun;
mod generic;
mod report_supported_operation_codes;
use std::{
fs::File,
io::Write,
sync::{Arc, Mutex},
};
use tempfile::tempfile;
use super::{
block_device::{
BlockDevice, BlockDeviceBackend, BlockOffset, BlockSize, ByteOffset, FileBackend,
},
target::EmulatedTarget,
};
use crate::scsi::{
sense::{self, SenseTriple},
CmdOutput, Request, Target, TaskAttr,
};
#[derive(Clone)]
struct TestBackend {
data: Arc<Mutex<[u8; 512 * 16]>>,
}
impl TestBackend {
fn new() -> Self {
TestBackend {
data: Arc::new(Mutex::new([0; 512 * 16])),
}
}
}
impl BlockDeviceBackend for TestBackend {
fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> std::io::Result<()> {
let data = self.data.lock().unwrap();
let offset = usize::try_from(u64::from(offset)).expect("offset should fit usize");
buf.copy_from_slice(&data[offset..(offset + buf.len())]);
Ok(())
}
fn write_exact_at(&mut self, buf: &[u8], offset: ByteOffset) -> std::io::Result<()> {
let mut data = self.data.lock().unwrap();
let offset = usize::try_from(u64::from(offset)).expect("offset should fit usize");
data[offset..(offset + buf.len())].copy_from_slice(buf);
Ok(())
}
fn size_in_blocks(&mut self) -> std::io::Result<BlockOffset> {
Ok(ByteOffset::from(
u64::try_from(self.data.lock().unwrap().len()).expect("size_in_blocks should fit u64"),
) / self.block_size())
}
fn block_size(&self) -> BlockSize {
BlockSize::try_from(512).expect("512 should be a valid BlockSize")
}
fn sync(&mut self) -> std::io::Result<()> {
Ok(())
}
}
fn null_image() -> FileBackend {
FileBackend::new(File::open("/dev/null").unwrap())
}
fn test_image() -> FileBackend {
let mut f = tempfile().unwrap();
// generate 16 512-byte sectors, each of which consist of a single
// repeated hex character, i.e.
// sector 00: 0000000....0000
// sector 15: fffffff....ffff
for chr in b'0'..=b'9' {
f.write_all(&[chr; 512]).unwrap();
}
for chr in b'a'..=b'f' {
f.write_all(&[chr; 512]).unwrap();
}
FileBackend::new(f)
}
fn do_command_in_lun(
target: &mut EmulatedTarget,
lun: u16,
cdb: &[u8],
data_out: &[u8],
expected_data_in: &[u8],
) {
let mut data_in = Vec::new();
let res = target.execute_command(
lun,
&mut &data_out[..],
&mut data_in,
Request {
id: 0,
cdb,
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
assert_eq!(res.unwrap(), CmdOutput::ok());
assert_eq!(&data_in, expected_data_in);
}
fn do_command_fail_lun(
target: &mut EmulatedTarget,
lun: u16,
cdb: &[u8],
expected_error: SenseTriple,
) {
let mut data_in = Vec::new();
let mut data_out: &[u8] = &[];
let res = target.execute_command(
lun,
&mut data_out,
&mut data_in,
Request {
id: 0,
cdb,
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
assert_eq!(res.unwrap(), CmdOutput::check_condition(expected_error));
assert_eq!(&data_in, &[]);
}
fn do_command_in(
target: &mut EmulatedTarget,
cdb: &[u8],
data_out: &[u8],
expected_data_in: &[u8],
) {
do_command_in_lun(target, 0, cdb, data_out, expected_data_in);
}
fn do_command_fail(target: &mut EmulatedTarget, cdb: &[u8], expected_error: SenseTriple) {
do_command_fail_lun(target, 0, cdb, expected_error);
}
fn block_size_512() -> BlockSize {
BlockSize::try_from(512).expect("512 should be a valid block_size")
}
#[test]
fn test_test_unit_ready() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(&mut target, &[0, 0, 0, 0, 0, 0], &[], &[]);
}
#[test]
fn test_report_luns() {
let mut target = EmulatedTarget::new();
for _ in 0..5 {
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
}
do_command_in(
&mut target,
&[
0xa0, // REPORT LUNS
0, // reserved
0, // select report
0, 0, 0, // reserved
0, 0, 1, 0, // alloc length: 256
0, 0,
],
&[],
&[
0, 0, 0, 40, // length: 5*8 = 40
0, 0, 0, 0, // reserved
0, 0, 0, 0, 0, 0, 0, 0, // LUN 0
0, 1, 0, 0, 0, 0, 0, 0, // LUN 1
0, 2, 0, 0, 0, 0, 0, 0, // LUN 2
0, 3, 0, 0, 0, 0, 0, 0, // LUN 3
0, 4, 0, 0, 0, 0, 0, 0, // LUN 4
],
);
}
#[test]
fn test_read_10() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 5, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
&[],
&[b'5'; 512],
);
}
#[test]
fn test_read_10_last_block() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 15, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
&[],
&[b'f'; 512],
);
}
#[test]
fn test_read_10_out_of_range() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_fail(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 16, // LBA: 16
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
);
}
#[test]
fn test_read_10_cross_out() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_fail(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 15, // LBA: 15
0, // reserved, group #
0, 2, // transfer length: 2
0, // control
],
sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
);
}
#[test]
fn test_write_10() {
let mut target = EmulatedTarget::new();
let mut backend = TestBackend::new();
let dev = BlockDevice::new(backend.clone());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
{
let data_out = [b'w'; 512];
do_command_in(
&mut target,
&[
0x2a, // WRITE (10)
0, // flags
0, 0, 0, 5, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
&data_out,
&[],
);
let mut buf = [0_u8; 512];
backend
.read_exact_at(&mut buf, BlockOffset::from(5) * block_size_512())
.expect("Reading should work");
assert_eq!(data_out, buf);
}
}
#[test]
fn test_write_same_16() {
let mut target = EmulatedTarget::new();
let mut backend = TestBackend::new();
let dev = BlockDevice::new(backend.clone());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
backend
.write_exact_at(&[0xff; 512 * 6], BlockOffset::from(5) * block_size_512())
.expect("Write should succeed");
let data_out = [0_u8; 512];
do_command_in(
&mut target,
&[
0x93, // WRITE SAME (16)
0, // flags
0, 0, 0, 0, 0, 0, 0, 5, // LBA: 5
0, 0, 0, 5, // tnumber of blocks: 5
0, // reserved, group #
0, // control
],
&data_out,
&[],
);
let mut buf = [0_u8; 512 * 5];
backend
.read_exact_at(&mut buf, BlockOffset::from(5) * block_size_512())
.expect("Reading should work");
assert_eq!([0_u8; 512 * 5], buf, "5 sectors should have been zero'd");
let mut buf = [0_u8; 512];
backend
.read_exact_at(&mut buf, BlockOffset::from(10) * block_size_512())
.expect("Reading should work");
assert_eq!(
[0xff_u8; 512], buf,
"sector after write should be left untouched"
);
}
#[test]
fn test_read_capacity_10() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
// TODO: we should test behavior with ≥ 2 TiB images. But not sure how we
// can do that reliably without risking using 2 TiB of disk
do_command_in(
&mut target,
&[
0x25, // READ CAPACITY (10)
0, 0, 0, 0, 0, 0, 0, 0, // flags
0, // control
],
&[],
&[
0, 0, 0, 15, // returned LBA (last valid LBA),
0, 0, 2, 0, // block size (512)
],
);
}
#[test]
fn test_read_capacity_16() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x9e, 0x10, // READ CAPACITY (16)
0, 0, 0, 0, 0, 0, 0, 0, // obsolete
0, 0, 0, 32, // allocation length: 32
0, // obselete/reserved
0, // control
],
&[],
&[
0, 0, 0, 0, 0, 0, 0, 15, // returned LBA (last valid LBA),
0, 0, 2, 0, // block size (512)
0, // reserved, zoned stuff, protection stuff
0, // one PB per LB
0xc0, // thin provisioning, unmapped blocks read 0
0, // LBA 0 is aligned (top bits above)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // reserved
],
);
}
#[test]
fn test_inquiry() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0x12, // INQUIRY
0, // EVPD bit: 0
0, // page code
1, 0, // alloc length: 256
0, // control
],
&[],
// some empty comments to get rustfmt to do something vaguely sensible
&[
0, // accessible; direct acccess block device
0, // features
0x7, // version
0x12, // response data format v2, HiSup = 1
91, // addl length
0, 0, 0, // unsupported features
// vendor
b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', //
// product
b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's',
b'i', b' ', //
// revision
b'v', b'0', b' ', b' ', //
// reserved/obselete/vendor specific
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// version descriptors
0x0, 0xc0, // SAM-6
0x05, 0xc0, // SPC-5 (no code assigned for 6 yet)
0x06, 0, // SBC-4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
// reserved
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
);
}
#[test]
fn test_request_sense() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0x3, // INQUIRY
0, // desc bit: 0
0, 0, // reserved
255, // alloc length
0, // control
],
&[],
// We'll always return this - modern SCSI has autosense, so any errors are sent with the
// response to the command that caused them (and therefore immediately cleared), and
// REQUEST SENSE returns an actual error only under some exceptional circumstances
// we don't implement.
&sense::NO_ADDITIONAL_SENSE_INFORMATION.to_fixed_sense(),
);
}
#[test]
fn test_request_sense_descriptor_format() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0x3, // INQUIRY
1, // desc bit: 1
0, 0, // reserved
255, // alloc length
0, // control
],
// We don't support descriptor format sense data.
sense::INVALID_FIELD_IN_CDB,
);
}