qemu_command_builder/args/
virtfs.rs1use crate::args::fsdev::SecurityModel;
2use crate::parsers::{ARG_VIRTFS, DELIM_COMMA};
3use crate::to_command::{ToArg, ToCommand};
4use bon::Builder;
5use proptest_derive::Arbitrary;
6use std::path::PathBuf;
7use std::str::FromStr;
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
10pub enum RemapForbidWarn {
11 Remap,
12 Forbid,
13 Warn,
14}
15
16impl ToArg for RemapForbidWarn {
17 fn to_arg(&self) -> &str {
18 match self {
19 RemapForbidWarn::Remap => "remap",
20 RemapForbidWarn::Forbid => "forbid",
21 RemapForbidWarn::Warn => "warn",
22 }
23 }
24}
25
26#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
27pub struct Local {
28 path: PathBuf,
29 mount_tag: String,
30 security_mode: SecurityModel,
31 id: Option<String>,
32 writeout: Option<()>,
33 readonly: Option<bool>,
34 fmode: Option<String>,
35 dmode: Option<String>,
36 multidevs: Option<RemapForbidWarn>,
37}
38
39#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
40pub struct Synth {
41 mount_tag: String,
42 id: Option<String>,
43 readonly: Option<bool>,
44}
45
46#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
47pub enum Virtfs {
48 Local(Local),
49 Synth(Synth),
50}
51impl ToCommand for Virtfs {
52 fn command(&self) -> String {
53 ARG_VIRTFS.to_string()
54 }
55 fn to_args(&self) -> Vec<String> {
56 let mut args = vec![];
57
58 match self {
59 Virtfs::Local(local) => {
60 args.push("local".to_string());
61 args.push(format!("path={}", local.path.display()));
62 args.push(format!("mount_tag={}", local.mount_tag));
63 args.push(format!("security_model={}", local.security_mode.to_arg()));
64 if let Some(id) = &local.id {
65 args.push(format!("id={}", id));
66 }
67 if local.writeout.is_some() {
68 args.push("writeout=immediate".to_string());
69 }
70 if let Some(readonly) = &local.readonly
71 && *readonly
72 {
73 args.push("readonly=on".to_string());
74 }
75 if let Some(fmode) = &local.fmode {
76 args.push(format!("fmode={}", fmode));
77 }
78 if let Some(dmode) = &local.dmode {
79 args.push(format!("dmode={}", dmode));
80 }
81 if let Some(multidevs) = &local.multidevs {
82 args.push(format!("multidevs={}", multidevs.to_arg()));
83 }
84 }
85 Virtfs::Synth(synth) => {
86 args.push("synth".to_string());
87 args.push(format!("mount_tag={}", synth.mount_tag));
88 if let Some(id) = &synth.id {
89 args.push(format!("id={}", id));
90 }
91 if let Some(readonly) = &synth.readonly
92 && *readonly
93 {
94 args.push("readonly=on".to_string());
95 }
96 }
97 }
98
99 vec![args.join(DELIM_COMMA)]
100 }
101}
102
103impl FromStr for Virtfs {
104 type Err = String;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 let mut parts = s.split(DELIM_COMMA);
108 let backend = parts.next().ok_or_else(|| "empty -virtfs argument".to_string())?;
109 match backend {
110 "local" => {
111 let mut path = None;
112 let mut mount_tag = None;
113 let mut security_mode = None;
114 let mut id = None;
115 let mut writeout = None;
116 let mut readonly = None;
117 let mut fmode = None;
118 let mut dmode = None;
119 let mut multidevs = None;
120
121 for part in parts {
122 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid virtfs local option: {part}"))?;
123 match key {
124 "path" => path = Some(PathBuf::from(value)),
125 "mount_tag" => mount_tag = Some(value.to_string()),
126 "security_model" => security_mode = Some(value.parse::<SecurityModel>().map_err(|e| e.to_string())?),
127 "id" => id = Some(value.to_string()),
128 "writeout" => {
129 if value != "immediate" {
130 return Err(format!("invalid writeout value: {value}"));
131 }
132 writeout = Some(());
133 }
134 "readonly" => {
135 if value != "on" {
136 return Err(format!("invalid readonly value: {value}"));
137 }
138 readonly = Some(true);
139 }
140 "fmode" => fmode = Some(value.to_string()),
141 "dmode" => dmode = Some(value.to_string()),
142 "multidevs" => {
143 multidevs = Some(match value {
144 "remap" => RemapForbidWarn::Remap,
145 "forbid" => RemapForbidWarn::Forbid,
146 "warn" => RemapForbidWarn::Warn,
147 _ => return Err(format!("invalid multidevs value: {value}")),
148 })
149 }
150 other => return Err(format!("unsupported virtfs local option: {other}")),
151 }
152 }
153
154 Ok(Self::Local(Local {
155 path: path.ok_or_else(|| "virtfs local requires path=".to_string())?,
156 mount_tag: mount_tag.ok_or_else(|| "virtfs local requires mount_tag=".to_string())?,
157 security_mode: security_mode.ok_or_else(|| "virtfs local requires security_model=".to_string())?,
158 id,
159 writeout,
160 readonly,
161 fmode,
162 dmode,
163 multidevs,
164 }))
165 }
166 "synth" => {
167 let mut mount_tag = None;
168 let mut id = None;
169 let mut readonly = None;
170 for part in parts {
171 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid virtfs synth option: {part}"))?;
172 match key {
173 "mount_tag" => mount_tag = Some(value.to_string()),
174 "id" => id = Some(value.to_string()),
175 "readonly" => {
176 if value != "on" {
177 return Err(format!("invalid readonly value: {value}"));
178 }
179 readonly = Some(true);
180 }
181 other => return Err(format!("unsupported virtfs synth option: {other}")),
182 }
183 }
184 Ok(Self::Synth(Synth {
185 mount_tag: mount_tag.ok_or_else(|| "virtfs synth requires mount_tag=".to_string())?,
186 id,
187 readonly,
188 }))
189 }
190 other => Err(format!("unsupported virtfs backend: {other}")),
191 }
192 }
193}