1#[allow(dead_code)]
2enum Priv {
3 Bind {
4 src: String,
5 rw: bool,
6 ignore_nonexist: bool,
7 recursive: bool,
8 },
9 Tmpfs {
10 rw: bool,
11 opts: Vec<String>,
12 },
13 Normal {
14 src: String,
15 rw: bool,
16 ignore_nonexist: bool,
17 opts: Vec<String>,
18 },
19}
20
21pub struct Mount(Priv);
23
24impl Mount {
25 #[cfg(feature = "systemd_233")]
33 pub fn bind<T: AsRef<str>>(src: T) -> Self {
34 Self(Priv::Bind {
35 src: src.as_ref().to_owned(),
36 rw: false,
37 recursive: false,
38 ignore_nonexist: false,
39 })
40 }
41
42 #[cfg(feature = "systemd_238")]
50 pub fn tmpfs() -> Self {
51 Self(Priv::Tmpfs {
52 rw: false,
53 opts: vec![],
54 })
55 }
56
57 #[cfg(feature = "systemd_247")]
65 pub fn normal<T: AsRef<str>>(src: T) -> Self {
66 Self(Priv::Normal {
67 src: src.as_ref().to_owned(),
68 rw: false,
69 ignore_nonexist: false,
70 opts: vec![],
71 })
72 }
73
74 pub fn writable(self) -> Self {
76 match self {
77 Self(Priv::Bind {
78 src,
79 recursive,
80 ignore_nonexist,
81 ..
82 }) => Self(Priv::Bind {
83 src,
84 recursive,
85 ignore_nonexist,
86 rw: true,
87 }),
88 Self(Priv::Tmpfs { opts, .. }) => Self(Priv::Tmpfs { opts, rw: true }),
89 Self(Priv::Normal {
90 opts,
91 src,
92 ignore_nonexist,
93 ..
94 }) => Self(Priv::Normal {
95 opts,
96 src,
97 ignore_nonexist,
98 rw: true,
99 }),
100 }
101 }
102
103 pub fn recursive(self) -> Self {
106 match self {
107 Self(Priv::Bind { src, rw, .. }) => Self(Priv::Bind {
108 src,
109 rw,
110 recursive: true,
111 ignore_nonexist: true,
112 }),
113 _ => self,
114 }
115 }
116
117 pub fn opt<T: AsRef<str>>(self, option: T) -> Option<Self> {
124 let o = option.as_ref();
125 if o.contains(',') {
126 return None;
127 }
128 match o {
129 "" | "ro" | "rw" => return None,
130 _ => {}
131 }
132 match self {
133 Self(Priv::Bind { .. }) => None,
134 Self(Priv::Tmpfs { rw, mut opts }) => {
135 opts.push(o.to_owned());
136 Some(Self(Priv::Tmpfs { rw, opts }))
137 }
138 Self(Priv::Normal {
139 src,
140 rw,
141 mut opts,
142 ignore_nonexist,
143 }) => {
144 opts.push(o.to_owned());
145 Some(Self(Priv::Normal {
146 src,
147 rw,
148 opts,
149 ignore_nonexist,
150 }))
151 }
152 }
153 }
154
155 pub fn ignore_nonexist(self) -> Self {
158 match self {
159 Self(Priv::Tmpfs { .. }) => self,
160 Self(Priv::Normal { src, rw, opts, .. }) => Self(Priv::Normal {
161 src,
162 rw,
163 opts,
164 ignore_nonexist: true,
165 }),
166 Self(Priv::Bind {
167 src, rw, recursive, ..
168 }) => Self(Priv::Bind {
169 src,
170 rw,
171 recursive,
172 ignore_nonexist: true,
173 }),
174 }
175 }
176}
177
178fn escape(s: &str) -> String {
179 s.replace('\\', "\\\\")
180 .replace(':', "\\:")
181 .replace(' ', "\\ ")
182}
183
184pub enum MarshaledMount {
185 Bind(String, String, bool, u64),
187 BindReadOnly(String, String, bool, u64),
189 Normal(String, String, bool, Vec<(&'static str, String)>),
192 Tmpfs(String, String),
194}
195
196pub fn marshal<T: AsRef<str>>(mount_point: T, mount: Mount) -> MarshaledMount {
197 use MarshaledMount::*;
198 let mp = escape(mount_point.as_ref());
199
200 match mount {
201 Mount(Priv::Bind {
202 src,
203 rw,
204 ignore_nonexist,
205 recursive,
206 }) => {
207 let src = escape(&src);
208 let flags: u64 = match recursive {
209 true => 16384, false => 0,
211 };
212 match rw {
213 true => Bind(src, mp, ignore_nonexist, flags),
214 false => BindReadOnly(src, mp, ignore_nonexist, flags),
215 }
216 }
217 Mount(Priv::Normal {
218 src,
219 rw,
220 ignore_nonexist,
221 mut opts,
222 }) => {
223 let src = escape(&src);
224 if !rw {
225 opts.push("ro".into());
226 }
227 let opts = opts.into_iter().map(|x| ("root", x)).collect();
228 Normal(src, mp, ignore_nonexist, opts)
229 }
230 Mount(Priv::Tmpfs { rw, mut opts }) => {
231 if !rw {
232 opts.push("ro".into());
233 }
234 Tmpfs(mp, opts.join(","))
235 }
236 }
237}