//! Virtual machine image handling. use log::debug; use std::fs::copy; use std::path::{Path, PathBuf}; use std::process::Command; use std::result::Result; /// Errors from this module. #[derive(Debug, thiserror::Error)] pub enum ImageError { /// The image file for a new VM already exists. #[error("image file already exists: {0}")] ImageExists(PathBuf), /// Error resizing image. #[error("qemu-img resize failed: {0}")] ResizeError(String), /// Base image copy error. #[error("could not copy base image {0} to {1}")] BaseImageCopy(PathBuf, PathBuf, #[source] std::io::Error), /// Could not execute command. #[error("couldn't execute {0}: {1}")] CommandError(String, #[source] std::io::Error), /// Error parsing a string as UTF8. #[error(transparent)] StringError(#[from] std::string::FromUtf8Error), } /// A virtual machine image. #[derive(Debug, Clone)] pub struct VirtualMachineImage { filename: PathBuf, } impl VirtualMachineImage { /// Create new image from a base image. pub fn new_from_base(base_image: &Path, image: &Path) -> Result { debug!( "creating new image {} from base image {}", image.display(), base_image.display() ); debug!("does {} exixt? {}", image.display(), image.exists()); if image.exists() { debug!("image already exists: {}", image.display()); return Err(ImageError::ImageExists(image.to_path_buf())); } debug!("copying base image to new image"); copy(base_image, image).map_err(|err| { ImageError::BaseImageCopy(base_image.to_path_buf(), image.to_path_buf(), err) })?; debug!("all good"); Ok(Self { filename: image.to_path_buf(), }) } /// Filename of the image. pub fn filename(&self) -> &Path { &self.filename } /// Change size of image. pub fn resize(&self, new_size: u64) -> Result<(), ImageError> { let r = Command::new("qemu-img") .arg("resize") .arg(self.filename()) .arg(format!("{}", new_size)) .output() .map_err(|err| ImageError::CommandError("qemu-img resize".to_string(), err))?; if !r.status.success() { let stderr = String::from_utf8(r.stderr)?; return Err(ImageError::ResizeError(stderr)); } Ok(()) } }