qemu_command_builder/
fsdev.rs

1use crate::to_command::ToArg;
2use crate::to_command::ToCommand;
3use bon::Builder;
4use std::path::PathBuf;
5
6pub enum SecurityModel {
7    Passthrough,
8    MappedXAttr,
9    MappedFile,
10    None,
11}
12
13impl ToArg for SecurityModel {
14    fn to_arg(&self) -> &str {
15        match self {
16            SecurityModel::Passthrough => "passthrough",
17            SecurityModel::MappedXAttr => "mapped-xattr",
18            SecurityModel::MappedFile => "mapped-file",
19            SecurityModel::None => "none",
20        }
21    }
22}
23/// Accesses to the filesystem are done by QEMU
24#[derive(Builder)]
25pub struct FsDevLocal {
26    /// Specifies identifier for this device.
27    id: String,
28
29    /// Specifies the export path for the file system device. Files
30    /// under this path will be available to the 9p client on the guest.
31    path: PathBuf,
32
33    /// Specifies the security model to be used for this export path.
34    /// Supported security models are "passthrough", "mapped-xattr",
35    /// "mapped-file" and "none". In "passthrough" security model, files
36    /// are stored using the same credentials as they are created on the
37    /// guest. This requires QEMU to run as root. In "mapped-xattr"
38    /// security model, some of the file attributes like uid, gid, mode
39    /// bits and link target are stored as file attributes. For
40    /// "mapped-file" these attributes are stored in the hidden
41    /// .virtfs\_metadata directory. Directories exported by this
42    /// security model cannot interact with other unix tools. "none"
43    /// security model is same as passthrough except the sever won't
44    /// report failures if it fails to set file attributes like
45    /// ownership. Security model is mandatory only for local fsdriver.
46    security_model: SecurityModel,
47
48    /// This is an optional argument. The only supported value is
49    /// "immediate". This means that host page cache will be used to
50    /// read and write data but write notification will be sent to the
51    /// guest only when the data has been reported as written by the
52    /// storage subsystem.
53    writeout: Option<()>,
54
55    /// Enables exporting 9p share as a readonly mount for guests. By
56    /// default read-write access is given.
57    readonly: Option<()>,
58
59    /// Specifies the default mode for newly created files on the host.
60    /// Works only with security models "mapped-xattr" and
61    /// "mapped-file".
62    fmode: Option<String>,
63
64    /// Specifies the default mode for newly created directories on the
65    /// host. Works only with security models "mapped-xattr" and
66    /// "mapped-file".
67    dmode: Option<String>,
68
69    /// Specify bandwidth throttling limits in bytes per second, either
70    /// for all request types or for reads or writes only.
71    throttling_bps_total: Option<usize>,
72    throttling_bps_read: Option<usize>,
73    throttling_bps_write: Option<usize>,
74
75    /// Specify bursts in bytes per second, either for all request types
76    /// or for reads or writes only. Bursts allow the guest I/O to spike
77    /// above the limit temporarily.
78    throttling_bps_total_max: Option<usize>,
79    bps_read_max: Option<usize>,
80    bps_write_max: Option<usize>,
81
82    /// Specify request rate limits in requests per second, either for
83    /// all request types or for reads or writes only.
84    throttling_iops_total: Option<usize>,
85    throttling_iops_read: Option<usize>,
86    throttling_iops_write: Option<usize>,
87
88    /// Specify bursts in requests per second, either for all request
89    /// types or for reads or writes only. Bursts allow the guest I/O to
90    /// spike above the limit temporarily.
91    throttling_iops_total_max: Option<usize>,
92    throttling_iops_read_max: Option<usize>,
93    throttling_iops_write_max: Option<usize>,
94
95    /// Let every is bytes of a request count as a new request for iops
96    /// throttling purposes.
97    throttling_iops_size: Option<usize>,
98}
99
100/// Synthetic filesystem, only used by QTests.
101pub struct FsDevSynth {
102    /// Specifies identifier for this device.
103    id: String,
104}
105
106/// Define a new file system device
107///
108/// TODO
109/// - device virtio-9p-type integration
110pub enum FsDev {
111    Local(FsDevLocal),
112    Synth(FsDevSynth),
113}
114
115impl ToCommand for FsDev {
116    fn to_command(&self) -> Vec<String> {
117        let mut cmd = vec![];
118
119        cmd.push("-fsdev".to_string());
120
121        match self {
122            FsDev::Local(local) => {
123                let mut arg = vec![];
124
125                arg.push(format!("local,id={}", local.id));
126
127                arg.push(format!(",path={}", local.path.display()));
128
129                arg.push(format!(",security-model={}", local.security_model.to_arg()));
130
131                if local.writeout.is_some() {
132                    arg.push("writeout=immediate".to_string());
133                }
134
135                if local.readonly.is_some() {
136                    arg.push("readonly=on".to_string());
137                }
138
139                if let Some(fmode) = &local.fmode {
140                    arg.push(format!("fmode={}", fmode));
141                }
142
143                if let Some(dmode) = &local.dmode {
144                    arg.push(format!("dmode={}", dmode));
145                }
146
147                if let Some(throttling_bps_total) = local.throttling_bps_total {
148                    arg.push(format!("throttling.bps-total={}", throttling_bps_total));
149                }
150                if let Some(throttling_bps_read) = local.throttling_bps_read {
151                    arg.push(format!("throttling.bps-read={}", throttling_bps_read));
152                }
153                if let Some(throttling_bps_write) = local.throttling_bps_write {
154                    arg.push(format!("throttling.bps-write={}", throttling_bps_write));
155                }
156
157                if let Some(throttling_bps_total_max) = local.throttling_bps_total_max {
158                    arg.push(format!(
159                        "throttling.bps-total-max={}",
160                        throttling_bps_total_max
161                    ));
162                }
163                if let Some(bps_read_max) = local.bps_read_max {
164                    arg.push(format!("bps-read-max={}", bps_read_max));
165                }
166                if let Some(bps_write_max) = local.bps_write_max {
167                    arg.push(format!("bps-write-max={}", bps_write_max));
168                }
169
170                if let Some(throttling_iops_total) = local.throttling_iops_total {
171                    arg.push(format!("throttling.iops-total={}", throttling_iops_total));
172                }
173                if let Some(throttling_iops_read) = local.throttling_iops_read {
174                    arg.push(format!("throttling.iops-read={}", throttling_iops_read));
175                }
176                if let Some(throttling_iops_write) = local.throttling_iops_write {
177                    arg.push(format!("throttling.iops-write={}", throttling_iops_write));
178                }
179
180                if let Some(throttling_ios_total_max) = local.throttling_iops_total_max {
181                    arg.push(format!(
182                        "throttling.ios-total-max={}",
183                        throttling_ios_total_max
184                    ));
185                }
186                if let Some(throttling_iops_read_max) = local.throttling_iops_read_max {
187                    arg.push(format!(
188                        "throttling.iops-read-max={}",
189                        throttling_iops_read_max
190                    ));
191                }
192                if let Some(throttling_iops_write_max) = local.throttling_iops_write_max {
193                    arg.push(format!(
194                        "throttling.iops-write-max={}",
195                        throttling_iops_write_max
196                    ));
197                }
198
199                if let Some(throttling_iops_size) = local.throttling_iops_size {
200                    arg.push(format!("throttling.iops-size={}", throttling_iops_size));
201                }
202                cmd.push(arg.join(","));
203            }
204            FsDev::Synth(synth) => {
205                let mut arg = String::new();
206                arg.push_str("synth,id=");
207                arg.push_str(synth.id.to_string().as_str());
208                cmd.push(arg);
209            }
210        }
211
212        cmd
213    }
214}