pv_core: add TemporaryDirectory

Add the type `TemporaryDirectory` that creates a temporary directory
that is automatically removed when it goes out of scope.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-By: Harald Freudenberger <freude@de.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2024-02-01 10:44:57 +01:00
committed by Jan Höppner
parent 17977eda30
commit e56acf4f14
2 changed files with 155 additions and 0 deletions

View File

@@ -15,6 +15,7 @@
mod error;
mod log;
mod macros;
mod tmpfile;
mod utils;
mod uvdevice;
mod uvsecret;
@@ -29,6 +30,8 @@ pub mod misc {
pub use crate::utils::{parse_hex, to_u16, to_u32, try_parse_u128, try_parse_u64};
pub use crate::utils::{read, write};
pub use crate::utils::{Flags, Lsb0Flags64, Msb0Flags64};
pub use crate::tmpfile::TemporaryDirectory;
}
/// Definitions and functions for interacting with the Ultravisor

152
rust/pv_core/src/tmpfile.rs Normal file
View File

@@ -0,0 +1,152 @@
use std::{
ffi::{CString, OsStr},
os::unix::prelude::OsStrExt,
path::{Path, PathBuf},
};
/// Rust wrapper for `libc::mkdtemp`
fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
let template_cstr = CString::new(template.as_ref().as_os_str().as_bytes())?;
let template_raw = template_cstr.into_raw();
unsafe {
// SAFETY: template_raw is a valid CString because it was generated by
// the `CString::new`.
let ret = libc::mkdtemp(template_raw);
if ret.is_null() {
Err(std::io::Error::last_os_error())
} else {
// SAFETY: `template_raw` is still a valid CString because it was
// generated by `CString::new` and modified by `libc::mkdtemp`.
let path_cstr = std::ffi::CString::from_raw(template_raw);
let path = OsStr::from_bytes(path_cstr.as_bytes());
let path = std::path::PathBuf::from(path);
Ok(path)
}
}
}
/// This type creates a temporary directory that is automatically removed when
/// it goes out of scope. It utilizes the `mkdtemp` function and its semantics,
/// with the addition of automatically including the template characters
/// `XXXXXX`.
#[derive(PartialEq, Eq, Debug)]
pub struct TemporaryDirectory {
path: Box<Path>,
}
impl TemporaryDirectory {
/// Creates a temporary directory using `prefix` as directory prefix.
///
/// # Errors
///
/// An error is returned if the temporary directory could not be created.
pub fn new<P: AsRef<Path>>(prefix: P) -> Result<Self, std::io::Error> {
let mut template = prefix.as_ref().to_owned();
let mut template_os_string = template.as_mut_os_string();
template_os_string.push("XXXXXX");
let temp_dir = mkdtemp(template_os_string)?;
Ok(Self {
path: temp_dir.into_boxed_path(),
})
}
/// Returns the path of the created temporary directory.
pub fn path(&self) -> &Path {
self.path.as_ref()
}
fn forget(mut self) {
self.path = PathBuf::new().into_boxed_path();
std::mem::forget(self);
}
/// Removes the created temporary directory and it's contents.
pub fn close(mut self) -> std::io::Result<()> {
let ret = std::fs::remove_dir_all(&self.path);
self.forget();
ret
}
}
impl AsRef<Path> for TemporaryDirectory {
fn as_ref(&self) -> &Path {
self.path()
}
}
impl Drop for TemporaryDirectory {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::{mkdtemp, TemporaryDirectory};
#[test]
fn mkdtemp_test() {
let template_inv_not_last_characters = "XXXXXXyay";
let template_inv_too_less_x = "yayXXXXX";
let template_inv_path_does_not_exist = "../NA-yay/XXXXXX";
let template = "yayXXXXXX";
let err = mkdtemp(template_inv_not_last_characters).expect_err("invalid template");
let err = mkdtemp(template_inv_too_less_x).expect_err("invalid template");
let err =
mkdtemp(template_inv_path_does_not_exist).expect_err("path does not exist template");
let path = mkdtemp(template).expect("mkdtemp should work");
assert!(path.exists());
assert!(path.as_os_str().to_str().expect("works").starts_with("yay"));
std::fs::remove_dir(path);
}
#[test]
fn temporary_directory_empty_name_test() {
let temp_dir = TemporaryDirectory::new("").expect("should work");
let path = temp_dir.path().to_owned();
assert!(path.exists());
// Test that close removes the directory
temp_dir.close();
assert!(!path.exists());
}
#[test]
fn temporary_directory_drop_test() {
let temp_dir = TemporaryDirectory::new("").expect("should work");
let path = temp_dir.path().to_owned();
assert!(path.exists());
// Test that the destructor removes the directory
drop(temp_dir);
assert!(!path.exists());
}
#[test]
fn temporary_directory_close_test() {
let temp_dir = TemporaryDirectory::new("yay").expect("should work");
let path = temp_dir.path().to_owned();
assert!(path.exists());
assert!(path.as_os_str().to_str().expect("works").starts_with("yay"));
// Test that close() removes the directory
temp_dir.close();
assert!(!path.exists());
}
#[test]
fn temporary_directory_as_ref_test() {
let temp_dir = TemporaryDirectory::new("").expect("should work");
assert_eq!(temp_dir.path(), temp_dir.as_ref());
}
}