169 lines
5.5 KiB
Rust
169 lines
5.5 KiB
Rust
use std::fs;
|
|
use std::path::{Path,PathBuf};
|
|
|
|
use crate::{Realm, Result, util};
|
|
use crate::Exec;
|
|
use crate::realm::config::OverlayType;
|
|
|
|
const REALMS_BASE_PATH: &str = "/realms";
|
|
const REALMS_RUN_PATH: &str = "/run/citadel/realms";
|
|
|
|
pub struct RealmOverlay {
|
|
realm: String,
|
|
overlay: OverlayType,
|
|
}
|
|
|
|
impl RealmOverlay {
|
|
|
|
pub fn remove_any_overlay(realm: &Realm) {
|
|
Self::try_remove(realm, OverlayType::Storage);
|
|
Self::try_remove(realm, OverlayType::TmpFS);
|
|
}
|
|
|
|
fn try_remove(realm: &Realm, overlay: OverlayType) {
|
|
let ov = Self::new(realm.name(), overlay);
|
|
if !ov.exists() {
|
|
return;
|
|
}
|
|
|
|
if let Err(e) = ov.remove() {
|
|
warn!("Error removing {:?} overlay for realm '{}': {}", overlay, realm.name(), e);
|
|
}
|
|
}
|
|
|
|
pub fn for_realm(realm: &Realm) -> Option<RealmOverlay> {
|
|
match realm.config().overlay() {
|
|
OverlayType::None => None,
|
|
overlay => Some(RealmOverlay::new(realm.name(), overlay)),
|
|
}
|
|
}
|
|
|
|
fn new(realm: &str, overlay: OverlayType) -> RealmOverlay {
|
|
let realm = realm.to_string();
|
|
RealmOverlay { realm, overlay }
|
|
}
|
|
|
|
|
|
/// Set up an overlayfs for a realm root filesystem either on tmpfs
|
|
/// or in a btrfs subvolume. Create the overlay over `lower` and
|
|
/// return the overlay mountpoint.
|
|
pub fn create(&self, lower: impl AsRef<Path>) -> Result<PathBuf> {
|
|
let lower = lower.as_ref();
|
|
info!("Creating overlay [{:?}] over rootfs mounted at {}", self.overlay, lower.display());
|
|
match self.overlay {
|
|
OverlayType::TmpFS => self.create_tmpfs(lower),
|
|
OverlayType::Storage => self.create_btrfs(lower),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// Remove a previously created realm overlay and return the
|
|
/// initial `lower` directory.
|
|
pub fn remove(&self) -> Result<PathBuf> {
|
|
let base = self.overlay_directory();
|
|
let mountpoint = base.join("mountpoint");
|
|
if !self.umount_overlay() {
|
|
warn!("Failed to unmount overlay mountpoint {}",mountpoint.display());
|
|
}
|
|
|
|
let lower = base.join("lower").read_link()
|
|
.map_err(context!("unable to read link to 'lower' directory of overlay"));
|
|
|
|
match self.overlay {
|
|
OverlayType::TmpFS => self.remove_tmpfs(&base)?,
|
|
OverlayType::Storage => self.remove_btrfs(&base)?,
|
|
_ => unreachable!(),
|
|
};
|
|
Ok(lower?)
|
|
}
|
|
|
|
pub fn exists(&self) -> bool {
|
|
self.overlay_directory().exists()
|
|
}
|
|
|
|
pub fn lower(&self) -> Option<PathBuf> {
|
|
let path = self.overlay_directory().join("lower");
|
|
if path.exists() {
|
|
fs::read_link(path).ok()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn remove_tmpfs(&self, base: &Path) -> Result<()> {
|
|
fs::remove_dir_all(base)
|
|
.map_err(context!("failed to remove overlay directory {:?}", base))
|
|
}
|
|
|
|
fn remove_btrfs(&self, base: &Path) -> Result<()> {
|
|
Exec::new("/usr/bin/btrfs")
|
|
.quiet()
|
|
.run(format!("subvolume delete {}", base.display()))
|
|
.map_err(context!("failed to remove btrfs subvolume {:?}", base))
|
|
}
|
|
|
|
fn create_tmpfs(&self, lower: &Path) -> Result<PathBuf> {
|
|
let base = self.overlay_directory();
|
|
if base.exists() {
|
|
info!("tmpfs overlay directory already exists, removing it before setting up overlay");
|
|
self.umount_overlay();
|
|
self.remove_tmpfs(&base)?;
|
|
}
|
|
self.setup_overlay(&base, lower)
|
|
}
|
|
|
|
fn umount_overlay(&self) -> bool {
|
|
let mountpoint = self.overlay_directory().join("mountpoint");
|
|
match cmd_ok!("/usr/bin/umount", "{}", mountpoint.display()) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
warn!("Could not run /usr/bin/umount on {}: {}", mountpoint.display(), e);
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
fn create_btrfs(&self, lower: &Path) -> Result<PathBuf> {
|
|
let subvolume = self.overlay_directory();
|
|
if subvolume.exists() {
|
|
info!("btrfs overlay subvolume already exists, removing it before setting up overlay");
|
|
self.umount_overlay();
|
|
self.remove_btrfs(&subvolume)?;
|
|
}
|
|
Exec::new("/usr/bin/btrfs").quiet().run(format!("subvolume create {}", subvolume.display()))?;
|
|
self.setup_overlay(&subvolume, lower)
|
|
}
|
|
|
|
fn setup_overlay(&self, base: &Path, lower: &Path) -> Result<PathBuf> {
|
|
let upper = self.mkdir(base, "upperdir")?;
|
|
let work = self.mkdir(base, "workdir")?;
|
|
let mountpoint = self.mkdir(base, "mountpoint")?;
|
|
let baselower = base.join("lower");
|
|
util::symlink(lower, &baselower)?;
|
|
cmd!("/usr/bin/mount",
|
|
"-t overlay realm-{}-overlay -olowerdir={},upperdir={},workdir={} {}",
|
|
self.realm,
|
|
lower.display(),
|
|
upper.display(),
|
|
work.display(),
|
|
mountpoint.display())?;
|
|
Ok(mountpoint)
|
|
}
|
|
|
|
fn mkdir(&self, base: &Path, dirname: &str) -> Result<PathBuf> {
|
|
let path = base.join(dirname);
|
|
util::create_dir(&path)?;
|
|
Ok(path)
|
|
}
|
|
|
|
fn overlay_directory(&self) -> PathBuf {
|
|
let base = match self.overlay {
|
|
OverlayType::TmpFS => REALMS_RUN_PATH,
|
|
OverlayType::Storage => REALMS_BASE_PATH,
|
|
_ => unreachable!(),
|
|
};
|
|
Path::new(base)
|
|
.join(format!("realm-{}", self.realm))
|
|
.join("overlay")
|
|
}
|
|
} |