#![cfg(target_os = "windows")]
#![allow(unsafe_code)]
use std::io;
use std::path::{Path, PathBuf};
use crate::windows::wclayer::{self, LayerChain};
#[derive(Debug)]
pub struct WritableLayer {
layer_path: PathBuf,
mount_path: String,
prepared: bool,
activated: bool,
needs_destroy: bool,
}
impl WritableLayer {
#[must_use]
pub fn layer_path(&self) -> &Path {
&self.layer_path
}
#[must_use]
pub fn vhd_mount_path(&self) -> &str {
&self.mount_path
}
pub fn detach_and_destroy(mut self) -> io::Result<()> {
let unprepare_res = if self.prepared {
wclayer::unprepare_layer(&self.layer_path)
} else {
Ok(())
};
self.prepared = false;
let deactivate_res = if self.activated {
wclayer::deactivate_layer(&self.layer_path)
} else {
Ok(())
};
self.activated = false;
let destroy_res = if self.needs_destroy {
wclayer::destroy_layer(&self.layer_path)
} else {
Ok(())
};
self.needs_destroy = false;
unprepare_res.and(deactivate_res).and(destroy_res)
}
}
impl Drop for WritableLayer {
fn drop(&mut self) {
if self.prepared {
if let Err(e) = wclayer::unprepare_layer(&self.layer_path) {
tracing::warn!(
layer = %self.layer_path.display(),
error = %e,
"unprepare_layer failed on drop",
);
}
}
if self.activated {
if let Err(e) = wclayer::deactivate_layer(&self.layer_path) {
tracing::warn!(
layer = %self.layer_path.display(),
error = %e,
"deactivate_layer failed on drop",
);
}
}
if self.needs_destroy {
if let Err(e) = wclayer::destroy_layer(&self.layer_path) {
tracing::warn!(
layer = %self.layer_path.display(),
error = %e,
"destroy_layer failed on drop",
);
}
}
}
}
pub fn create(layer_path: &Path, parent_chain: &LayerChain) -> io::Result<WritableLayer> {
std::fs::create_dir_all(layer_path)?;
if let Err(e) = wclayer::create_sandbox_layer(layer_path, parent_chain) {
cleanup_best_effort(layer_path, false, false);
return Err(e);
}
if let Err(e) = wclayer::activate_layer(layer_path) {
cleanup_best_effort(layer_path, false, false);
return Err(e);
}
if let Err(e) = wclayer::prepare_layer(layer_path, parent_chain) {
cleanup_best_effort(layer_path, false, true);
return Err(e);
}
let mount_path = match wclayer::get_layer_mount_path(layer_path) {
Ok(p) => p,
Err(e) => {
cleanup_best_effort(layer_path, true, true);
return Err(e);
}
};
Ok(WritableLayer {
layer_path: layer_path.to_path_buf(),
mount_path,
prepared: true,
activated: true,
needs_destroy: true,
})
}
pub fn create_unactivated(
layer_path: &Path,
parent_chain: &LayerChain,
) -> io::Result<WritableLayer> {
std::fs::create_dir_all(layer_path)?;
if let Err(e) = wclayer::create_sandbox_layer(layer_path, parent_chain) {
cleanup_best_effort(layer_path, false, false);
return Err(e);
}
Ok(WritableLayer {
layer_path: layer_path.to_path_buf(),
mount_path: String::new(),
prepared: false,
activated: false,
needs_destroy: true,
})
}
fn cleanup_best_effort(layer_path: &Path, prepared: bool, activated: bool) {
if prepared {
if let Err(e) = wclayer::unprepare_layer(layer_path) {
tracing::warn!(
layer = %layer_path.display(),
error = %e,
"unprepare_layer failed during rollback",
);
}
}
if activated {
if let Err(e) = wclayer::deactivate_layer(layer_path) {
tracing::warn!(
layer = %layer_path.display(),
error = %e,
"deactivate_layer failed during rollback",
);
}
}
if let Err(e) = wclayer::destroy_layer(layer_path) {
tracing::warn!(
layer = %layer_path.display(),
error = %e,
"destroy_layer failed during rollback",
);
}
}