use std::path::{Path, PathBuf};
use boxlite_shared::errors::{BoxliteError, BoxliteResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum DiskFormat {
Ext4,
Qcow2,
}
pub struct Disk {
path: PathBuf,
#[allow(dead_code)]
format: DiskFormat,
persistent: bool,
virtual_size: u64,
on_disk_size: u64,
}
impl Disk {
pub fn new(path: PathBuf, format: DiskFormat, persistent: bool) -> Self {
Self {
path,
format,
persistent,
virtual_size: 0,
on_disk_size: 0,
}
}
pub fn with_sizes(
path: PathBuf,
format: DiskFormat,
persistent: bool,
virtual_size: u64,
on_disk_size: u64,
) -> Self {
Self {
path,
format,
persistent,
virtual_size,
on_disk_size,
}
}
pub fn path(&self) -> &Path {
&self.path
}
#[allow(dead_code)]
pub fn format(&self) -> DiskFormat {
self.format
}
pub fn virtual_size(&self) -> u64 {
self.virtual_size
}
pub fn on_disk_size(&self) -> u64 {
self.on_disk_size
}
pub fn leak(self) -> PathBuf {
let path = self.path.clone();
std::mem::forget(self);
path
}
}
impl Drop for Disk {
fn drop(&mut self) {
if self.persistent {
tracing::debug!(
"Skipping cleanup for persistent disk: {}",
self.path.display()
);
return;
}
if self.path.exists() {
if let Err(e) = std::fs::remove_file(&self.path) {
tracing::warn!("Failed to cleanup disk {}: {}", self.path.display(), e);
} else {
tracing::debug!("Cleaned up disk: {}", self.path.display());
}
}
}
}
pub(crate) mod base_disk;
pub mod constants;
pub(crate) mod ext4;
pub(crate) mod qcow2;
pub(crate) use base_disk::{BaseDisk, BaseDiskKind, BaseDiskManager};
pub use ext4::{create_ext4_from_dir, inject_file_into_ext4};
pub use qcow2::{
BackingFormat, Qcow2Helper, is_backing_dependency, read_backing_chain, read_backing_file_path,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DiskInfo {
pub base_path: String,
pub container_disk_bytes: u64,
pub size_bytes: u64,
}
impl DiskInfo {
pub fn as_path(&self) -> &Path {
Path::new(&self.base_path)
}
pub fn to_path_buf(&self) -> PathBuf {
PathBuf::from(&self.base_path)
}
pub fn exists(&self) -> bool {
self.as_path().exists()
}
pub fn to_disk(&self, format: DiskFormat, persistent: bool) -> Disk {
Disk::with_sizes(
self.to_path_buf(),
format,
persistent,
self.container_disk_bytes,
self.size_bytes,
)
}
}
impl From<&Disk> for DiskInfo {
fn from(disk: &Disk) -> Self {
Self {
base_path: disk.path().to_string_lossy().to_string(),
container_disk_bytes: disk.virtual_size(),
size_bytes: disk.on_disk_size(),
}
}
}
pub(crate) fn fork_qcow2(source: &Path, dest: &Path) -> BoxliteResult<Disk> {
let virtual_size = Qcow2Helper::qcow2_virtual_size(source)?;
std::fs::rename(source, dest).map_err(|e| {
BoxliteError::Storage(format!(
"Failed to move disk {} to {}: {}",
source.display(),
dest.display(),
e
))
})?;
Qcow2Helper::create_cow_child_disk(dest, BackingFormat::Qcow2, source, virtual_size)?.leak();
let on_disk_size = std::fs::metadata(dest).map(|m| m.len()).unwrap_or(0);
Ok(Disk::with_sizes(
dest.to_path_buf(),
DiskFormat::Qcow2,
true,
virtual_size,
on_disk_size,
))
}