qemu_command_builder/blockdev.rs
1use std::collections::HashMap;
2
3use bon::Builder;
4
5use crate::common::{IgnoreUnmap, OnOff, OnOffUnmap};
6use crate::to_command::{ToArg, ToCommand};
7
8/// Define a new block driver node. Some of the options apply to all
9/// block drivers, other options are only accepted for a specific block
10/// driver. See below for a list of generic options and options for the
11/// most common block drivers.
12///
13/// Options that expect a reference to another node (e.g. ``file``) can
14/// be given in two ways. Either you specify the node name of an already
15/// existing node (file=node-name), or you define a new node inline,
16/// adding options for the referenced node after a dot
17/// (file.filename=path,file.aio=native).
18///
19/// A block driver node created with ``-blockdev`` can be used for a
20/// guest device by specifying its node name for the ``drive`` property
21/// in a ``-device`` argument that defines a block device.
22///
23/// TODO
24/// - constrain driver opts
25#[derive(Builder)]
26pub struct BlockDev {
27 /// Specifies the block driver to use for the given node.
28 pub driver: String,
29
30 /// This defines the name of the block driver node by which it
31 /// will be referenced later. The name must be unique, i.e. it
32 /// must not match the name of a different block driver node, or
33 /// (if you use ``-drive`` as well) the ID of a drive.
34 ///
35 /// If no node name is specified, it is automatically generated.
36 /// The generated node name is not intended to be predictable
37 /// and changes between QEMU invocations. For the top level, an
38 /// explicit node name must be specified.
39 pub node_name: Option<String>,
40
41 /// discard is one of "ignore" (or "off") or "unmap" (or "on")
42 /// and controls whether ``discard`` (also known as ``trim`` or
43 /// ``unmap``) requests are ignored or passed to the filesystem.
44 /// Some machine types may not support discard requests.
45 pub discard: Option<IgnoreUnmap>,
46
47 /// The host page cache can be avoided with ``cache.direct=on``.
48 /// This will attempt to do disk IO directly to the guest's
49 /// memory. QEMU may still perform an internal copy of the data.
50 pub cache_direct: Option<OnOff>,
51
52 /// In case you don't care about data integrity over host
53 /// failures, you can use ``cache.no-flush=on``. This option
54 /// tells QEMU that it never needs to write any data to the disk
55 /// but can instead keep things in cache. If anything goes
56 /// wrong, like your host losing power, the disk storage getting
57 /// disconnected accidentally, etc. your image will most
58 /// probably be rendered unusable.
59 pub cache_no_flush: Option<OnOff>,
60
61 /// Open the node read-only. Guest write attempts will fail.
62 ///
63 /// Note that some block drivers support only read-only access,
64 /// either generally or in certain configurations. In this case,
65 /// the default value ``read-only=off`` does not work and the
66 /// option must be specified explicitly.
67 pub read_only: Option<OnOff>,
68
69 /// If ``auto-read-only=on`` is set, QEMU may fall back to
70 /// read-only usage even when ``read-only=off`` is requested, or
71 /// even switch between modes as needed, e.g. depending on
72 /// whether the image file is writable or whether a writing user
73 /// is attached to the node.
74 pub auto_read_only: Option<OnOff>,
75
76 /// Override the image locking system of QEMU by forcing the
77 /// node to utilize weaker shared access for permissions where
78 /// it would normally request exclusive access. When there is
79 /// the potential for multiple instances to have the same file
80 /// open (whether this invocation of QEMU is the first or the
81 /// second instance), both instances must permit shared access
82 /// for the second instance to succeed at opening the file.
83 ///
84 /// Enabling ``force-share=on`` requires ``read-only=on``.
85 pub force_share: Option<OnOff>,
86
87 /// detect-zeroes is "off", "on" or "unmap" and enables the
88 /// automatic conversion of plain zero writes by the OS to
89 /// driver specific optimized zero write commands. You may even
90 /// choose "unmap" if discard is set to "unmap" to allow a zero
91 /// write to be converted to an ``unmap`` operation.
92 pub detect_zeroes: Option<OnOffUnmap>,
93
94 pub driver_opts: Option<HashMap<String, String>>,
95}
96
97impl ToCommand for BlockDev {
98 fn to_command(&self) -> Vec<String> {
99 let mut cmd = vec![];
100
101 cmd.push("-blockdev".to_string());
102
103 let mut args = vec![];
104
105 args.push(format!("driver={}", self.driver));
106 if let Some(node_name) = &self.node_name {
107 args.push(format!("node-name={}", node_name));
108 }
109 if let Some(discard) = &self.discard {
110 args.push(format!("discard={}", discard.to_arg()));
111 }
112 if let Some(cache_direct) = &self.cache_direct {
113 args.push(format!("cache.direct={}", cache_direct.to_arg()));
114 }
115 if let Some(cache_no_flush) = &self.cache_no_flush {
116 args.push(format!("cache.no-flush={}", cache_no_flush.to_arg()));
117 }
118 if let Some(read_only) = &self.read_only {
119 args.push(format!("read-only={}", read_only.to_arg()));
120 }
121 if let Some(auto_read_only) = &self.auto_read_only {
122 args.push(format!("auto-read-only={}", auto_read_only.to_arg()));
123 }
124 if let Some(force_share) = &self.force_share {
125 args.push(format!("force-share={}", force_share.to_arg()));
126 }
127 if let Some(detect_zeroes) = &self.detect_zeroes {
128 args.push(format!("detect-zeroes={}", detect_zeroes.to_arg()));
129 }
130 if let Some(driver_opts) = &self.driver_opts {
131 for (key, val) in driver_opts {
132 args.push(format!("{}={}", key, val));
133 }
134 }
135
136 cmd.push(args.join(","));
137
138 cmd
139 }
140}