1use std::fs;
2use std::path::{Path, PathBuf};
3
4use crate::error::Context;
5use crate::{Error, Result};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Overlay {
17 pub lower: Vec<PathBuf>,
18 pub upper: PathBuf,
19 pub work: PathBuf,
20 pub merged: PathBuf,
21 pub userxattr: bool,
23}
24
25impl Overlay {
26 pub fn prepare(lower: Vec<PathBuf>, dir: &Path) -> Result<Self> {
28 let ov = Self {
29 lower,
30 upper: dir.join("upper"),
31 work: dir.join("work"),
32 merged: dir.join("merged"),
33 userxattr: false,
34 };
35 ov.create()?;
36 Ok(ov)
37 }
38
39 pub fn for_layers(layers: &[PathBuf], dir: &Path, xattr: crate::OverlayXattr) -> Result<Self> {
43 let mut ov = Self::prepare(Self::stack(layers), dir)?;
44 ov.userxattr = xattr == crate::OverlayXattr::User;
45 Ok(ov)
46 }
47
48 pub fn stack(layers: &[PathBuf]) -> Vec<PathBuf> {
53 let mut lower: Vec<PathBuf> = Vec::with_capacity(layers.len());
54 for l in layers.iter().rev() {
55 if !lower.contains(l) {
56 lower.push(l.clone());
57 }
58 }
59 lower
60 }
61
62 pub fn create(&self) -> Result<()> {
65 if self.lower.is_empty() {
66 return Err(Error::Invalid("an overlay needs a lower directory".into()));
67 }
68 for d in [&self.upper, &self.work, &self.merged] {
69 fs::create_dir_all(d).ctx(|| format!("creating {}", d.display()))?;
70 }
71 Ok(())
72 }
73
74 pub fn mount_data(&self) -> String {
76 let lower: Vec<String> = self.lower.iter().map(|p| escape(p)).collect();
77 let mut data = format!(
78 "lowerdir={},upperdir={},workdir={}",
79 lower.join(":"),
80 escape(&self.upper),
81 escape(&self.work)
82 );
83 if self.userxattr {
84 data.push_str(",userxattr");
85 }
86 data
87 }
88}
89
90fn escape(p: &Path) -> String {
91 let s = p.display().to_string();
92 let mut out = String::with_capacity(s.len());
93 for c in s.chars() {
94 if matches!(c, '\\' | ',' | ':') {
95 out.push('\\');
96 }
97 out.push(c);
98 }
99 out
100}