bias_vfs/impls/
overlay.rs1use crate::error::VfsErrorKind;
4use crate::{FileSystem, SeekAndRead, SeekAndWrite, VfsMetadata, VfsPath, VfsResult};
5use std::collections::HashSet;
6use std::path::PathBuf;
7use std::time::SystemTime;
8
9#[derive(Debug, Clone)]
16pub struct OverlayFS {
17 layers: Vec<VfsPath>,
18}
19
20impl OverlayFS {
21 pub fn new(layers: &[VfsPath]) -> Self {
23 if layers.is_empty() {
24 panic!("OverlayFS needs at least one layer")
25 }
26 OverlayFS {
27 layers: layers.to_vec(),
28 }
29 }
30
31 fn write_layer(&self) -> &VfsPath {
32 &self.layers[0]
33 }
34
35 fn read_path(&self, path: &str) -> VfsResult<VfsPath> {
36 if path.is_empty() {
37 return Ok(self.layers[0].clone());
38 }
39 if self.whiteout_path(path)?.exists()? {
40 return Err(VfsErrorKind::FileNotFound.into());
41 }
42 for layer in &self.layers {
43 let layer_path = layer.join(&path[1..])?;
44 if layer_path.exists()? {
45 return Ok(layer_path);
46 }
47 }
48 let read_path = self.write_layer().join(&path[1..])?;
49 if !read_path.exists()? {
50 return Err(VfsErrorKind::FileNotFound.into());
51 }
52 Ok(read_path)
53 }
54
55 fn write_path(&self, path: &str) -> VfsResult<VfsPath> {
56 if path.is_empty() {
57 return Ok(self.layers[0].clone());
58 }
59 self.write_layer().join(&path[1..])
60 }
61
62 fn whiteout_path(&self, path: &str) -> VfsResult<VfsPath> {
63 if path.is_empty() {
64 return self.write_layer().join(".whiteout/_wo");
65 }
66 self.write_layer()
67 .join(format!(".whiteout/{}_wo", &path[1..]))
68 }
69
70 fn ensure_has_parent(&self, path: &str) -> VfsResult<()> {
71 let separator = path.rfind('/');
72 if let Some(index) = separator {
73 let parent_path = &path[..index];
74 if self.exists(parent_path)? {
75 self.write_path(parent_path)?.create_dir_all()?;
76 return Ok(());
77 }
78 }
79 Err(VfsErrorKind::Other("Parent path does not exist".into()).into())
80 }
81}
82
83impl FileSystem for OverlayFS {
84 fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
85 let actual_path = if !path.is_empty() { &path[1..] } else { path };
86 if !self.read_path(path)?.exists()? {
87 return Err(VfsErrorKind::FileNotFound.into());
88 }
89 let mut entries = HashSet::<String>::new();
90 for layer in &self.layers {
91 let layer_path = layer.join(actual_path)?;
92 if layer_path.exists()? {
93 for path in layer_path.read_dir()? {
94 entries.insert(path.filename());
95 }
96 }
97 }
98 let whiteout_path = self.write_layer().join(format!(".whiteout{}", path))?;
100 if whiteout_path.exists()? {
101 for path in whiteout_path.read_dir()? {
102 let filename = path.filename();
103 if filename.ends_with("_wo") {
104 entries.remove(&filename[..filename.len() - 3]);
105 }
106 }
107 }
108 Ok(Box::new(entries.into_iter()))
109 }
110
111 fn create_dir(&self, path: &str) -> VfsResult<()> {
112 self.ensure_has_parent(path)?;
113 self.write_path(path)?.create_dir()?;
114 let whiteout_path = self.whiteout_path(path)?;
115 if whiteout_path.exists()? {
116 whiteout_path.remove_file()?;
117 }
118 Ok(())
119 }
120
121 fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
122 self.read_path(path)?.open_file()
123 }
124
125 fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
126 self.ensure_has_parent(path)?;
127 let result = self.write_path(path)?.create_file()?;
128 let whiteout_path = self.whiteout_path(path)?;
129 if whiteout_path.exists()? {
130 whiteout_path.remove_file()?;
131 }
132 Ok(result)
133 }
134
135 fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
136 let write_path = self.write_path(path)?;
137 if !write_path.exists()? {
138 self.ensure_has_parent(path)?;
139 self.read_path(path)?.copy_file(&write_path)?;
140 }
141 write_path.append_file()
142 }
143
144 fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
145 self.read_path(path)?.metadata()
146 }
147
148 fn set_creation_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
149 self.write_path(path)?.set_creation_time(time)
150 }
151
152 fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
153 self.write_path(path)?.set_modification_time(time)
154 }
155
156 fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
157 self.write_path(path)?.set_access_time(time)
158 }
159
160 fn exists(&self, path: &str) -> VfsResult<bool> {
161 if self
162 .whiteout_path(path)
163 .map_err(|err| err.with_context(|| "whiteout_path"))?
164 .exists()?
165 {
166 return Ok(false);
167 }
168 self.read_path(path)
169 .map(|path| path.exists())
170 .unwrap_or(Ok(false))
171 }
172
173 fn remove_file(&self, path: &str) -> VfsResult<()> {
174 self.read_path(path)?;
176 let write_path = self.write_path(path)?;
177 if write_path.exists()? {
178 write_path.remove_file()?;
179 }
180 let whiteout_path = self.whiteout_path(path)?;
181 whiteout_path.parent().create_dir_all()?;
182 whiteout_path.create_file()?;
183 Ok(())
184 }
185
186 fn remove_dir(&self, path: &str) -> VfsResult<()> {
187 self.read_path(path)?;
189 let write_path = self.write_path(path)?;
190 if write_path.exists()? {
191 write_path.remove_dir()?;
192 }
193 let whiteout_path = self.whiteout_path(path)?;
194 whiteout_path.parent().create_dir_all()?;
195 whiteout_path.create_file()?;
196 Ok(())
197 }
198
199 fn real_path(&self, path: &str) -> VfsResult<PathBuf> {
200 let vspath = self.read_path(path)?;
201
202 vspath.filesystem().real_path(path)
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::MemoryFS;
210 test_vfs!({
211 let upper_root: VfsPath = MemoryFS::new().into();
212 let lower_root: VfsPath = MemoryFS::new().into();
213 OverlayFS::new(&[upper_root, lower_root])
214 });
215
216 fn create_roots() -> (VfsPath, VfsPath, VfsPath) {
217 let lower_root: VfsPath = MemoryFS::new().into();
218 let upper_root: VfsPath = MemoryFS::new().into();
219 let overlay_root: VfsPath =
220 OverlayFS::new(&[upper_root.clone(), lower_root.clone()]).into();
221 (lower_root, upper_root, overlay_root)
222 }
223
224 #[test]
225 fn read() -> VfsResult<()> {
226 let (lower_root, upper_root, overlay_root) = create_roots();
227 let lower_path = lower_root.join("foo.txt")?;
228 let upper_path = upper_root.join("foo.txt")?;
229 let overlay_path = overlay_root.join("foo.txt")?;
230 lower_path.create_file()?.write_all(b"Hello Lower")?;
231 assert_eq!(&overlay_path.read_to_string()?, "Hello Lower");
232 upper_path.create_file()?.write_all(b"Hello Upper")?;
233 assert_eq!(&overlay_path.read_to_string()?, "Hello Upper");
234 lower_path.remove_file()?;
235 assert_eq!(&overlay_path.read_to_string()?, "Hello Upper");
236 upper_path.remove_file()?;
237 assert!(!overlay_path.exists()?, "File should not exist anymore");
238 Ok(())
239 }
240
241 #[test]
242 fn read_dir() -> VfsResult<()> {
243 let (lower_root, upper_root, overlay_root) = create_roots();
244 upper_root.join("foo/upper")?.create_dir_all()?;
245 upper_root.join("foo/common")?.create_dir_all()?;
246 lower_root.join("foo/common")?.create_dir_all()?;
247 lower_root.join("foo/lower")?.create_dir_all()?;
248 let entries: Vec<_> = overlay_root.join("foo")?.read_dir()?.collect();
249 let mut paths: Vec<_> = entries.iter().map(|path| path.as_str()).collect();
250 paths.sort();
251 assert_eq!(paths, vec!["/foo/common", "/foo/lower", "/foo/upper"]);
252 Ok(())
253 }
254
255 #[test]
256 fn read_dir_root() -> VfsResult<()> {
257 let (lower_root, upper_root, overlay_root) = create_roots();
258 upper_root.join("upper")?.create_dir_all()?;
259 upper_root.join("common")?.create_dir_all()?;
260 lower_root.join("common")?.create_dir_all()?;
261 lower_root.join("lower")?.create_dir_all()?;
262 let entries: Vec<_> = overlay_root.read_dir()?.collect();
263 let mut paths: Vec<_> = entries.iter().map(|path| path.as_str()).collect();
264 paths.sort();
265 assert_eq!(paths, vec!["/common", "/lower", "/upper"]);
266 Ok(())
267 }
268
269 #[test]
270 fn create_dir() -> VfsResult<()> {
271 let (lower_root, _upper_root, overlay_root) = create_roots();
272 lower_root.join("foo")?.create_dir_all()?;
273 assert!(overlay_root.join("foo")?.exists()?, "dir should exist");
274 overlay_root.join("foo/bar")?.create_dir()?;
275 assert!(overlay_root.join("foo/bar")?.exists()?, "dir should exist");
276 Ok(())
277 }
278
279 #[test]
280 fn create_file() -> VfsResult<()> {
281 let (lower_root, _upper_root, overlay_root) = create_roots();
282 lower_root.join("foo")?.create_dir_all()?;
283 assert!(overlay_root.join("foo")?.exists()?, "dir should exist");
284 overlay_root.join("foo/bar")?.create_file()?;
285 assert!(overlay_root.join("foo/bar")?.exists()?, "file should exist");
286 Ok(())
287 }
288
289 #[test]
290 fn append_file() -> VfsResult<()> {
291 let (lower_root, _upper_root, overlay_root) = create_roots();
292 lower_root.join("foo")?.create_dir_all()?;
293 lower_root
294 .join("foo/bar.txt")?
295 .create_file()?
296 .write_all(b"Hello Lower\n")?;
297 overlay_root
298 .join("foo/bar.txt")?
299 .append_file()?
300 .write_all(b"Hello Overlay\n")?;
301 assert_eq!(
302 &overlay_root.join("foo/bar.txt")?.read_to_string()?,
303 "Hello Lower\nHello Overlay\n"
304 );
305 Ok(())
306 }
307
308 #[test]
309 fn remove_file() -> VfsResult<()> {
310 let (lower_root, _upper_root, overlay_root) = create_roots();
311 lower_root.join("foo")?.create_dir_all()?;
312 lower_root
313 .join("foo/bar.txt")?
314 .create_file()?
315 .write_all(b"Hello Lower\n")?;
316 assert!(
317 overlay_root.join("foo/bar.txt")?.exists()?,
318 "file should exist"
319 );
320
321 overlay_root.join("foo/bar.txt")?.remove_file()?;
322 assert!(
323 !overlay_root.join("foo/bar.txt")?.exists()?,
324 "file should not exist anymore"
325 );
326
327 overlay_root
328 .join("foo/bar.txt")?
329 .create_file()?
330 .write_all(b"Hello Overlay\n")?;
331 assert!(
332 overlay_root.join("foo/bar.txt")?.exists()?,
333 "file should exist"
334 );
335 assert_eq!(
336 &overlay_root.join("foo/bar.txt")?.read_to_string()?,
337 "Hello Overlay\n"
338 );
339 Ok(())
340 }
341
342 #[test]
343 fn remove_dir() -> VfsResult<()> {
344 let (lower_root, _upper_root, overlay_root) = create_roots();
345 lower_root.join("foo")?.create_dir_all()?;
346 lower_root.join("foo/bar")?.create_dir_all()?;
347 assert!(overlay_root.join("foo/bar")?.exists()?, "dir should exist");
348
349 overlay_root.join("foo/bar")?.remove_dir()?;
350 assert!(
351 !overlay_root.join("foo/bar")?.exists()?,
352 "dir should not exist anymore"
353 );
354
355 overlay_root.join("foo/bar")?.create_dir()?;
356 assert!(overlay_root.join("foo/bar")?.exists()?, "dir should exist");
357 Ok(())
358 }
359
360 #[test]
361 fn read_dir_removed_entries() -> VfsResult<()> {
362 let (lower_root, _upper_root, overlay_root) = create_roots();
363 lower_root.join("foo")?.create_dir_all()?;
364 lower_root.join("foo/bar")?.create_dir_all()?;
365 lower_root.join("foo/bar.txt")?.create_dir_all()?;
366
367 let entries: Vec<_> = overlay_root.join("foo")?.read_dir()?.collect();
368 let mut paths: Vec<_> = entries.iter().map(|path| path.as_str()).collect();
369 paths.sort();
370 assert_eq!(paths, vec!["/foo/bar", "/foo/bar.txt"]);
371 overlay_root.join("foo/bar")?.remove_dir()?;
372 overlay_root.join("foo/bar.txt")?.remove_file()?;
373
374 let entries: Vec<_> = overlay_root.join("foo")?.read_dir()?.collect();
375 let mut paths: Vec<_> = entries.iter().map(|path| path.as_str()).collect();
376 paths.sort();
377 assert_eq!(paths, vec![] as Vec<&str>);
378
379 Ok(())
380 }
381}
382
383#[cfg(test)]
384mod tests_physical {
385 use super::*;
386 use crate::PhysicalFS;
387 test_vfs!({
388 let temp_dir = std::env::temp_dir();
389 let dir = temp_dir.join(uuid::Uuid::new_v4().to_string());
390 let lower_path = dir.join("lower");
391 std::fs::create_dir_all(&lower_path).unwrap();
392 let upper_path = dir.join("upper");
393 std::fs::create_dir_all(&upper_path).unwrap();
394
395 let upper_root: VfsPath = PhysicalFS::new(upper_path).into();
396 let lower_root: VfsPath = PhysicalFS::new(lower_path).into();
397 OverlayFS::new(&[upper_root, lower_root])
398 });
399}
400
401#[cfg(test)]
402mod tests_overlay {
403 use super::*;
404 use crate::{AltrootFS, PhysicalFS};
405
406 #[test]
407 fn test_real_paths() {
408 let temp_dir = std::env::temp_dir();
409 let dir = temp_dir.join(uuid::Uuid::new_v4().to_string());
410 let lower_path = dir.join("overlay1").join("lower");
411 std::fs::create_dir_all(&lower_path).unwrap();
412 let upper_path = dir.join("overlay2").join("upper");
413 std::fs::create_dir_all(&upper_path).unwrap();
414
415 let root1 = dir.join("overlay1");
416 let root2 = dir.join("overlay2");
417
418 let upper_root: VfsPath = AltrootFS::new(PhysicalFS::new(root1).into()).into();
419 let lower_root: VfsPath = AltrootFS::new(PhysicalFS::new(root2).into()).into();
420
421 let fs = OverlayFS::new(&[upper_root, lower_root]);
422
423 assert_eq!(fs.real_path("/lower").unwrap(), lower_path);
424 assert_eq!(fs.real_path("/upper").unwrap(), upper_path);
425 }
426}