use blkpath::ResolveDevice;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
#[derive(Debug)]
pub struct CachedDevice {
pub path: PathBuf,
pub file: File,
}
impl CachedDevice {
fn new(path: PathBuf) -> io::Result<Self> {
let file = OpenOptions::new()
.read(true)
.custom_flags(libc::O_DIRECT)
.open(&path)?;
Ok(Self { path, file })
}
}
static DEVICE_CACHE: Lazy<RwLock<HashMap<u64, Arc<CachedDevice>>>> =
Lazy::new(|| RwLock::new(HashMap::new()));
pub fn get_or_create_cached_device(file: &File) -> io::Result<Arc<CachedDevice>> {
let dev_id = file.metadata()?.dev();
{
let cache = DEVICE_CACHE.read().unwrap();
if let Some(entry) = cache.get(&dev_id) {
return Ok(Arc::clone(entry));
}
}
let device_path = file.resolve_device()?;
let mut cache = DEVICE_CACHE.write().unwrap();
if let Some(entry) = cache.get(&dev_id) {
return Ok(Arc::clone(entry));
}
let entry = Arc::new(CachedDevice::new(device_path)?);
cache.insert(dev_id, Arc::clone(&entry));
Ok(entry)
}
pub fn open_device_uncached(file: &File) -> io::Result<CachedDevice> {
let device_path = file.resolve_device()?;
CachedDevice::new(device_path)
}
#[cfg(test)]
pub fn clear_cache() {
let mut cache = DEVICE_CACHE.write().unwrap();
cache.clear();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_operations() {
clear_cache();
}
}