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; 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.entries[entry_index_within_block] = dir_entry; } None => { return Err(ENOENT); } } } Ok(()) } /// 删除第 entry_index 个 dir entry pub(crate) fn remove_direntry( &mut self, parent_index: usize, parent_inode: &mut Inode, name: &OsStr, 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).is_some() { Ok(()) } else { Err(ENOENT) } } else { Err(ENOENT) // let block_index_within_inode = (entry_index / 15) as usize; // let entry_index_within_block = (entry_index % 15) as usize; // // match self.access_block_mut::(parent_inode, block_index_within_inode) { // Some(directory_block) => { // if directory_block.block.query(entry_index_within_block) { // directory_block.block.deallocate(entry_index_within_block); // // directory_block.block.entries[entry_index_within_block] = // // DirectoryEntry::default(); // Ok(()) // } else { // Err(ENOENT) // } // } // None => 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) } // // 找到第一个有空闲 DirEntry 的块, 从中分配一个 entry // let mut block_index_within_inode: usize = 0; // loop { // // 所有已经分配的块都用完, 需要额外分配一个块了 // if block_index_within_inode as u32 == parent_inode.n_blocks { // if self.allocate_block_for(parent_inode).is_none() { // return Err(ENOSPC); // } // } // // 寻找当前块里有没有空闲空间 // if let Some(directory_block) = // self.access_block_mut::(parent_inode, block_index_within_inode) // { // if let Some(entry_index_within_block) = directory_block.block.allocate() { // // 如果有空闲空间, 可以分配一个块 // let name_len = child_inode_name.len() as u8; // let mut name = [0u8; 256]; // (&mut name[0..name_len as usize]) // .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, // }; // directory_block.block.entries[entry_index_within_block] = dir_entry; // let entry_index = block_index_within_inode * 15 + entry_index_within_block; // parent_inode.size += 1; // return Ok(entry_index as u32); // } // } // block_index_within_inode += 1; // } } 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) // let block_index_within_inode = (entry_index / 15) as usize; // let entry_index_within_block = (entry_index % 15) as usize; // // match self.access_block::(parent_inode, block_index_within_inode) { // Some(directory_block) => { // if directory_block.block.query(entry_index_within_block) { // Ok(directory_block.block.entries[entry_index_within_block].clone()) // } else { // Err(ENOENT) // } // } // None => Err(ENOENT), // } } } // TODO 实现一个带 cache 的版本 /// 返回 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) } } }