use {fuse, IdGenerator};
use nodes::{ArcNode, Cache, Dir, File, Symlink};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
#[derive(Default)]
pub struct NoCache {
}
impl Cache for NoCache {
fn get_or_create(&self, ids: &IdGenerator, underlying_path: &Path, attr: &fs::Metadata,
writable: bool) -> ArcNode {
if attr.is_dir() {
Dir::new_mapped(ids.next(), underlying_path, attr, writable)
} else if attr.file_type().is_symlink() {
Symlink::new_mapped(ids.next(), underlying_path, attr, writable)
} else {
File::new_mapped(ids.next(), underlying_path, attr, writable)
}
}
fn delete(&self, _path: &Path, _file_type: fuse::FileType) {
}
fn rename(&self, _old_path: &Path, _new_path: PathBuf, _file_type: fuse::FileType) {
}
}
#[derive(Default)]
pub struct PathCache {
entries: Mutex<HashMap<PathBuf, ArcNode>>,
}
impl Cache for PathCache {
fn get_or_create(&self, ids: &IdGenerator, underlying_path: &Path, attr: &fs::Metadata,
writable: bool) -> ArcNode {
if attr.is_dir() {
return Dir::new_mapped(ids.next(), underlying_path, attr, writable);
}
let mut entries = self.entries.lock().unwrap();
if let Some(node) = entries.get(underlying_path) {
if node.writable() == writable {
return node.clone();
}
info!("Missed node caching opportunity because writability has changed for {:?}",
underlying_path)
}
let node: ArcNode = if attr.is_dir() {
panic!("Directory entries cannot be cached and are handled above");
} else if attr.file_type().is_symlink() {
Symlink::new_mapped(ids.next(), underlying_path, attr, writable)
} else {
File::new_mapped(ids.next(), underlying_path, attr, writable)
};
entries.insert(underlying_path.to_path_buf(), node.clone());
node
}
fn delete(&self, path: &Path, file_type: fuse::FileType) {
let mut entries = self.entries.lock().unwrap();
if file_type == fuse::FileType::Directory {
debug_assert!(!entries.contains_key(path), "Directories are not currently cached");
} else {
entries.remove(path).expect("Tried to delete unknown path from the cache");
}
}
fn rename(&self, old_path: &Path, new_path: PathBuf, file_type: fuse::FileType) {
let mut entries = self.entries.lock().unwrap();
if file_type == fuse::FileType::Directory {
debug_assert!(!entries.contains_key(old_path), "Directories are not currently cached");
} else {
let node = entries.remove(old_path).expect("Tried to rename unknown path in the cache");
entries.insert(new_path, node);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use testutils;
#[test]
fn path_cache_behavior() {
let root = tempdir().unwrap();
let dir1 = root.path().join("dir1");
fs::create_dir(&dir1).unwrap();
let dir1attr = fs::symlink_metadata(&dir1).unwrap();
let file1 = root.path().join("file1");
drop(fs::File::create(&file1).unwrap());
let file1attr = fs::symlink_metadata(&file1).unwrap();
let file2 = root.path().join("file2");
drop(fs::File::create(&file2).unwrap());
let file2attr = fs::symlink_metadata(&file2).unwrap();
let ids = IdGenerator::new(1);
let cache = PathCache::default();
assert_eq!(1, cache.get_or_create(&ids, &dir1, &dir1attr, false).inode());
assert_eq!(2, cache.get_or_create(&ids, &dir1, &dir1attr, false).inode());
assert_eq!(3, cache.get_or_create(&ids, &dir1, &dir1attr, true).inode());
assert_eq!(4, cache.get_or_create(&ids, &file1, &file1attr, false).inode());
assert_eq!(5, cache.get_or_create(&ids, &file2, &file2attr, true).inode());
assert_eq!(6, cache.get_or_create(&ids, &file1, &file1attr, true).inode());
assert_eq!(7, cache.get_or_create(&ids, &file2, &file2attr, false).inode());
assert_eq!(6, cache.get_or_create(&ids, &file1, &file1attr, true).inode());
assert_eq!(7, cache.get_or_create(&ids, &file2, &file2attr, false).inode());
assert_eq!(8, cache.get_or_create(&ids, &file1, &file1attr, false).inode());
assert_eq!(9, cache.get_or_create(&ids, &file2, &file2attr, true).inode());
}
#[test]
fn path_cache_nodes_support_all_file_types() {
let ids = IdGenerator::new(1);
let cache = PathCache::default();
for (_fuse_type, path) in testutils::AllFileTypes::new().entries {
let fs_attr = fs::symlink_metadata(&path).unwrap();
cache.get_or_create(&ids, &path, &fs_attr, false);
}
}
}