use crate::disk::block::{DirectoryBlock, DirectoryEntry}; use crate::disk::inode::Inode; use crate::AyaFS; use indexmap::IndexMap; use libc::{c_int, ENOENT, ENOSPC}; use log::debug; use std::ffi::{OsStr, OsString}; use std::os::unix::ffi::OsStrExt; use indexmap::map::Entry::Occupied; impl AyaFS { pub(crate) fn init_direntry_map(&mut self, index: usize) { let direntry_map: IndexMap = IndexMap::new(); if let Some((old_inode_index, old_dir_entry_map)) = self.dir_entry_map.push(index, direntry_map) { if let Some(old_inode) = self.get_inode(old_inode_index) { let mut old_inode = old_inode.clone(); self.write_back_direntry(old_inode_index, &mut old_inode, old_dir_entry_map) .unwrap(); self.update_inode(old_inode_index, old_inode); } } } pub(crate) fn load_direntry_map(&mut self, index: usize, inode: &Inode) -> Result<(), c_int> { if self.dir_entry_map.contains(&index) { debug!("load_direntry_map(ino: {}) already in cache", index); return Ok(()); } debug!("load_direntry_map(ino: {}) loading", index); let mut dir_entry_map: IndexMap = IndexMap::new(); let mut entry_index: usize = 0; loop { let block_index_within_inode = entry_index / 15; let entry_index_within_block = entry_index % 15; match self.access_block::(inode, block_index_within_inode) { Some(directory_block) => { if directory_block.block.query(entry_index_within_block) { let dir_entry = &directory_block.block.entries[entry_index_within_block]; let name = dir_entry.name(); dir_entry_map.insert(name, dir_entry.clone()); } else { break; } } None => break, } entry_index += 1; } if let Some((old_inode_index, old_dir_entry_map)) = self.dir_entry_map.push(index, dir_entry_map) { if let Some(old_inode) = self.get_inode(old_inode_index) { let mut old_inode = old_inode.clone(); self.write_back_direntry(old_inode_index, &mut old_inode, old_dir_entry_map)?; self.update_inode(old_inode_index, old_inode); } } Ok(()) } pub(crate) fn write_back_direntry( &mut self, _parent_index: usize, parent_inode: &mut Inode, dir_entry_map: IndexMap, ) -> Result<(), c_int> { for (entry_index, (_, dir_entry)) in dir_entry_map.into_iter().enumerate() { let block_index_within_inode = entry_index / 15; let entry_index_within_block = entry_index % 15; // 不够就新分配 if self .get_block_index(parent_inode, block_index_within_inode) .is_none() { match self.allocate_block_for(parent_inode) { Some((_, block_index_within)) => { assert_eq!(block_index_within, block_index_within_inode); } None => { return Err(ENOSPC); } } } match self.access_block_mut::(parent_inode, block_index_within_inode) { Some(directory_block) => { directory_block.block.allocate(entry_index_within_block); directory_block.block.entries[entry_index_within_block] = dir_entry; } None => { return Err(ENOENT); } } } Ok(()) } pub(crate) fn exchange_direntry>( &mut self, parent_index: usize, parent_inode: &mut Inode, name: T, new_parent_index: usize, new_parent_inode: &mut Inode, new_name: T, ) -> Result<(), c_int> { self.load_direntry_map(parent_index, parent_inode)?; self.load_direntry_map(new_parent_index, new_parent_inode)?; let dir_entry = self.dir_entry_map .get(&parent_index) .ok_or(ENOENT)? .get(name.as_ref()) .cloned() .ok_or(ENOENT)?; let new_dir_entry = self.dir_entry_map .get(&new_parent_index) .ok_or(ENOENT)? .get(new_name.as_ref()) .cloned() .ok_or(ENOENT)?; let name: &OsStr = name.as_ref(); let new_name: &OsStr = new_name.as_ref(); self.dir_entry_map .get_mut(&parent_index) .map(|dir_entry_map| { if let Occupied(mut entry) = dir_entry_map.entry(name.to_os_string()) { *entry.get_mut() = new_dir_entry.clone(); } }) .ok_or(ENOENT)?; self.dir_entry_map .get_mut(&new_parent_index) .map(|new_dir_entry_map| { if let Occupied(mut entry) = new_dir_entry_map.entry(new_name.to_os_string()) { *entry.get_mut() = dir_entry.clone(); } }) .ok_or(ENOENT)?; Ok(()) } /// 删除第 entry_index 个 dir entry pub(crate) fn remove_direntry>( &mut self, parent_index: usize, parent_inode: &mut Inode, name: T, _entry_index: u32, ) -> Result<(), c_int> { self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get_mut(&parent_index) { debug!(" remove_direntry(ino: {}) using hashmap", parent_index); if dir_entry_map.shift_remove(name.as_ref()).is_some() { Ok(()) } else { Err(ENOENT) } } else { Err(ENOENT) } } pub(crate) fn add_direntry_2>( &mut self, parent_index: usize, parent_inode: &mut Inode, child_inode_name: T, dir_entry: DirectoryEntry, ) -> Result { let child_inode_name = child_inode_name.as_ref(); self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get_mut(&parent_index) { let (entry_index, _) = dir_entry_map.insert_full(child_inode_name.to_os_string(), dir_entry); debug!( " add_direntry(ino: {}) using hashmap, entry {}", parent_index, entry_index ); parent_inode.size += 1; Ok(entry_index as u32) } else { Err(ENOENT) } } pub(crate) fn add_direntry>( &mut self, parent_index: usize, parent_inode: &mut Inode, child_inode_index: usize, child_inode_name: T, file_type: u8, ) -> Result { let child_inode_name = child_inode_name.as_ref(); self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get_mut(&parent_index) { let name_len = child_inode_name.len() as u8; let mut name = [0u8; 256]; (&mut name[0..child_inode_name.len()]).copy_from_slice(child_inode_name.as_bytes()); let dir_entry = DirectoryEntry { inode: child_inode_index as u32, record_len: 264, name_len, file_type, name, }; let (entry_index, _) = dir_entry_map.insert_full(child_inode_name.to_os_string(), dir_entry); debug!( " add_direntry(ino: {}) using hashmap, entry {}", parent_index, entry_index ); parent_inode.size += 1; Ok(entry_index as u32) } else { Err(ENOENT) } } pub(crate) fn get_direntry_by_name>( &mut self, parent_index: usize, parent_inode: &Inode, name: T, ) -> Result { self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get(&parent_index) { debug!( " get_direntry(ino: {}, name: {:?}) using hashmap", parent_index, name.as_ref() ); dir_entry_map .get(name.as_ref()) .cloned() .ok_or(ENOENT) } else { Err(ENOENT) } } pub(crate) fn get_direntry( &mut self, parent_index: usize, parent_inode: &Inode, entry_index: u32, ) -> Result { self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get(&parent_index) { debug!( " get_direntry(ino: {}, entry_index: {}) using hashmap", parent_index, entry_index ); dir_entry_map .iter() .skip(entry_index as usize) .next() .map(|entry| entry.1.clone()) .ok_or(ENOENT) } else { Err(ENOENT) } } /// 返回 inode_index, inode 在 parent 里的 index, inode 本身 pub fn lookup_name( &mut self, parent_index: usize, parent_inode: &Inode, name: &OsStr, ) -> Result<(u32, u32, Inode), c_int> { self.load_direntry_map(parent_index, parent_inode)?; if let Some(dir_entry_map) = self.dir_entry_map.get(&parent_index) { if let Some((entry_index, _, dir_entry)) = dir_entry_map.get_full(name) { let inode_index = dir_entry.inode; let inode = self.get_inode(inode_index as usize).unwrap().clone(); Ok((inode_index, entry_index as u32, inode)) } else { Err(ENOENT) } } else { let mut entry_index = 0; while entry_index < parent_inode.size { if let Ok(entry) = self.get_direntry(parent_index, parent_inode, entry_index) { if entry.name() == name { let inode = self.get_inode(entry.inode as usize).unwrap().clone(); return Ok((entry.inode, entry_index, inode)); } } entry_index += 1; } Err(ENOENT) } } }