devices/virtio/scsi/
mod.rs1use std::path::PathBuf;
6
7use anyhow::Context;
8use serde::Deserialize;
9use serde::Serialize;
10
11use crate::virtio::VirtioDevice;
12use crate::VirtioDeviceArgs;
13use crate::VirtioDeviceModule;
14
15pub(crate) mod sys;
16
17pub mod commands;
18pub mod constants;
19mod device;
20
21pub use device::Controller;
22pub use device::DiskConfig;
23
24fn scsi_option_lock_default() -> bool {
25 true
26}
27fn scsi_option_block_size_default() -> u32 {
28 512
29}
30
31#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, serde_keyvalue::FromKeyValues)]
33#[serde(deny_unknown_fields, rename_all = "kebab-case")]
34pub struct ScsiOption {
35 pub path: PathBuf,
37 #[serde(default, rename = "ro")]
39 pub read_only: bool,
40 #[serde(default = "scsi_option_lock_default")]
42 pub lock: bool,
43 #[serde(default = "scsi_option_block_size_default")]
45 pub block_size: u32,
46 #[serde(default)]
49 pub root: bool,
50}
51
52#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
54pub struct VirtioScsiModule {
55 pub disks: Vec<ScsiOption>,
56}
57
58impl VirtioScsiModule {
59 pub fn new(disks: Vec<ScsiOption>) -> Self {
60 Self { disks }
61 }
62}
63
64impl VirtioDeviceModule for VirtioScsiModule {
65 fn sort_name(&self) -> &'static str {
66 "scsi"
67 }
68
69 fn create(&self, args: &mut VirtioDeviceArgs<'_>) -> anyhow::Result<Box<dyn VirtioDevice>> {
70 let base_features = crate::virtio::base_features(args.protection_type);
71 let disks = self
72 .disks
73 .iter()
74 .map(|op| {
75 base::info!("Trying to attach a scsi device: {}", op.path.display());
76 Ok(DiskConfig {
77 file: op.open()?,
78 block_size: op.block_size,
79 read_only: op.read_only,
80 })
81 })
82 .collect::<anyhow::Result<Vec<_>>>()?;
83 let controller =
84 Controller::new(base_features, disks).context("failed to create a scsi controller")?;
85 Ok(Box::new(controller))
86 }
87
88 #[cfg(any(target_os = "android", target_os = "linux"))]
89 fn create_jail(
90 &self,
91 jail_config: &jail::JailConfig,
92 ) -> anyhow::Result<Option<minijail::Minijail>> {
93 let jail = jail::simple_jail(
94 Some(jail_config),
95 &crate::virtio::VirtioDeviceType::Regular.seccomp_policy_file("scsi"),
96 )?;
97 Ok(jail)
98 }
99}
100
101#[cfg(test)]
102impl Default for ScsiOption {
103 fn default() -> Self {
104 Self {
105 path: PathBuf::new(),
106 read_only: false,
107 lock: scsi_option_lock_default(),
108 block_size: scsi_option_block_size_default(),
109 root: false,
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use std::path::Path;
117
118 use serde_keyvalue::from_key_values;
119
120 use super::*;
121
122 #[test]
123 fn parse_scsi_options() {
124 let scsi_option = from_key_values::<ScsiOption>("/path/to/image").unwrap();
125 assert_eq!(
126 scsi_option,
127 ScsiOption {
128 path: Path::new("/path/to/image").to_path_buf(),
129 ..Default::default()
130 }
131 );
132
133 let scsi_option = from_key_values::<ScsiOption>("/path/to/image,ro").unwrap();
134 assert_eq!(
135 scsi_option,
136 ScsiOption {
137 path: Path::new("/path/to/image").to_path_buf(),
138 read_only: true,
139 ..Default::default()
140 }
141 );
142
143 let scsi_option = from_key_values::<ScsiOption>("/path/to/image,block-size=1024").unwrap();
144 assert_eq!(
145 scsi_option,
146 ScsiOption {
147 path: Path::new("/path/to/image").to_path_buf(),
148 block_size: 1024,
149 ..Default::default()
150 }
151 );
152
153 let scsi_option =
154 from_key_values::<ScsiOption>("/path/to/image,block-size=1024,root").unwrap();
155 assert_eq!(
156 scsi_option,
157 ScsiOption {
158 path: Path::new("/path/to/image").to_path_buf(),
159 block_size: 1024,
160 root: true,
161 ..Default::default()
162 }
163 );
164 }
165}