use crate::config;
use crate::error::FpgadError;
use crate::platforms::platform::OverlayHandler;
use crate::system_io::{fs_create_dir, fs_read, fs_remove_dir, fs_write};
use log::{info, trace};
use std::path::{Path, PathBuf};
fn construct_overlay_fs_path(overlay_handle: &str) -> PathBuf {
let overlay_fs_path = PathBuf::from(config::OVERLAY_CONTROL_DIR).join(overlay_handle);
trace!("overlay_fs_path will be {overlay_fs_path:?}");
overlay_fs_path
}
#[derive(Debug)]
pub struct UniversalOverlayHandler {
overlay_fs_path: PathBuf,
}
impl UniversalOverlayHandler {
fn get_vfs_status(&self) -> Result<String, FpgadError> {
let status_path = self.overlay_fs_path()?.join("status");
trace!("Reading from {status_path:?}");
fs_read(&status_path).map(|s| s.trim_end_matches('\n').to_string())
}
fn get_vfs_path(&self) -> Result<PathBuf, FpgadError> {
let path_path = self.overlay_fs_path()?.join("path");
trace!("Reading from {path_path:?}");
let path_string = fs_read(&path_path).map(|s| s.trim_end_matches('\n').to_string())?;
Ok(PathBuf::from(path_string))
}
fn vfs_check_applied(&self, source_path_rel: &Path) -> Result<(), FpgadError> {
let path_file_contents = &self.get_vfs_path()?;
if path_file_contents.ends_with(source_path_rel) {
info!("overlay path contents is valid: '{path_file_contents:?}'");
} else {
return Err(FpgadError::OverlayStatus(format!(
"When trying to apply overlay '{source_path_rel:?}', the resulting vfs path contained '{path_file_contents:?}'"
)));
}
let status = self.status()?;
match status.contains("applied") {
true => {
info!("overlay status is 'applied'")
}
false => {
return Err(FpgadError::OverlayStatus(format!(
"After writing to configfs, overlay status does not show 'applied'. Instead it is '{status}'"
)));
}
}
Ok(())
}
}
impl OverlayHandler for UniversalOverlayHandler {
fn apply_overlay(&self, source_path_rel: &Path) -> Result<(), FpgadError> {
let overlay_fs_path = self.overlay_fs_path()?;
if overlay_fs_path.exists() {
return Err(FpgadError::Argument(format!(
"Overlay with this handle already exists at {overlay_fs_path:?}. \
Remove the overlay and try again."
)));
}
fs_create_dir(overlay_fs_path)?;
trace!("Created dir {overlay_fs_path:?}");
let overlay_path_file = overlay_fs_path.join("path");
if !overlay_path_file.exists() {
return Err(FpgadError::Internal(format!(
"Overlay at {overlay_fs_path:?} did not initialise a new overlay: \
the `path` virtual file did not get created by the kernel. \
Is the parent dir mounted as a configfs directory?"
)));
}
match fs_write(&overlay_path_file, false, source_path_rel.to_string_lossy()) {
Ok(_) => {
trace!("'{source_path_rel:?}' successfully written to {overlay_path_file:?}");
}
Err(e) => return Err(e),
}
self.vfs_check_applied(source_path_rel)
}
fn remove_overlay(&self) -> Result<(), FpgadError> {
let overlay_fs_path = self.overlay_fs_path()?;
fs_remove_dir(overlay_fs_path)
}
fn required_flags(&self) -> Result<isize, FpgadError> {
Ok(0)
}
fn status(&self) -> Result<String, FpgadError> {
if !self.overlay_fs_path()?.exists() {
return Ok("not present".into());
};
let path = self.get_vfs_path()?;
let status = self.get_vfs_status()?;
Ok(format!("{path:?} {status}"))
}
fn overlay_fs_path(&self) -> Result<&Path, FpgadError> {
Ok(self.overlay_fs_path.as_path())
}
}
impl UniversalOverlayHandler {
pub(crate) fn new(overlay_handle: &str) -> Self {
UniversalOverlayHandler {
overlay_fs_path: construct_overlay_fs_path(overlay_handle),
}
}
}