From ec2f349a648e4d87fba12d20e338b1cd6d8ef29a Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Mon, 27 Nov 2023 00:11:55 -0800 Subject: Fix directory stuff --- src/filesystem/trait_impl.rs | 207 +++++++++++++++++++++++++++++++++---------- 1 file changed, 162 insertions(+), 45 deletions(-) (limited to 'src/filesystem') diff --git a/src/filesystem/trait_impl.rs b/src/filesystem/trait_impl.rs index 636bae7..5353f6e 100644 --- a/src/filesystem/trait_impl.rs +++ b/src/filesystem/trait_impl.rs @@ -6,11 +6,12 @@ use crate::{AyaFS, TTL}; use fuser::TimeOrNow::{Now, SpecificTime}; use fuser::{ Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, - ReplyLseek, ReplyOpen, Request, TimeOrNow, + ReplyLseek, ReplyOpen, ReplyWrite, Request, TimeOrNow, }; use libc::{ - c_int, EACCES, EEXIST, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR, EPERM, R_OK, - S_ISGID, S_ISUID, S_IXGRP, S_IXOTH, S_IXUSR, W_OK, + c_int, EACCES, EBADF, EEXIST, EINVAL, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR, + ENOTEMPTY, EPERM, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY, R_OK, S_ISGID, S_ISUID, S_IXGRP, + S_IXOTH, S_IXUSR, W_OK, }; use log::debug; use std::ffi::OsStr; @@ -46,7 +47,7 @@ impl Filesystem for AyaFS { R_OK, ) { let parent_inode = parent_inode.clone(); - match self.lookup_name(&parent_inode, name) { + match self.lookup_name(parent, &parent_inode, name) { Ok((inode_index, _, inode)) => { let attr = to_fileattr(inode_index as usize, &inode); reply.entry(&TTL, &attr, 0); @@ -62,7 +63,6 @@ impl Filesystem for AyaFS { } fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { - debug!("Filesystem::getattr(ino: {})", ino); if let Some(inode) = self.get_inode(ino as usize) { reply.attr(&TTL, &to_fileattr(ino as usize, inode)); } else { @@ -284,7 +284,10 @@ impl Filesystem for AyaFS { let mut parent_inode = parent_inode.clone(); // 如果已经存在, 返回 already exists - if self.lookup_name(&parent_inode, name).is_ok() { + if self + .lookup_name(parent as usize, &parent_inode, name) + .is_ok() + { reply.error(EEXIST); return; } @@ -295,9 +298,13 @@ impl Filesystem for AyaFS { { let child_inode = child_inode.clone(); let file_attr = to_fileattr(child_inode_index, &child_inode); - if let Err(err_code) = - self.add_direntry(&mut parent_inode, child_inode_index, name, &child_inode) - { + if let Err(err_code) = self.add_direntry( + parent as usize, + &mut parent_inode, + child_inode_index, + name, + child_inode.mode.into(), + ) { reply.error(err_code); return; } @@ -350,20 +357,27 @@ impl Filesystem for AyaFS { let mut parent_inode = parent_inode.clone(); // 已经存在 -> File exists - if self.lookup_name(&parent_inode, name).is_ok() { + if self + .lookup_name(parent as usize, &parent_inode, name) + .is_ok() + { reply.error(EEXIST); return; } let mode = mode as u16; if let Some((child_inode_index, child_inode)) = - self.create_directory(mode, req.uid(), req.gid(), 0, Some(parent as u32)) + self.create_directory(mode, req.uid(), req.gid(), 0, Some(parent as usize)) { let child_inode = child_inode.clone(); let file_attr = to_fileattr(child_inode_index, &child_inode); - if let Err(err_code) = - self.add_direntry(&mut parent_inode, child_inode_index, name, &child_inode) - { + if let Err(err_code) = self.add_direntry( + parent as usize, + &mut parent_inode, + child_inode_index, + name, + child_inode.mode.into(), + ) { reply.error(err_code); return; } @@ -409,7 +423,8 @@ impl Filesystem for AyaFS { let mut parent_inode = parent_inode.clone(); // 不存在 -> No such file or directory - if let Ok((inode_index, entry_index, mut inode)) = self.lookup_name(&parent_inode, name) + if let Ok((inode_index, entry_index, mut inode)) = + self.lookup_name(parent as usize, &parent_inode, name) { let inode_index = inode_index as usize; // 要删除的 entry 是目录, 不能用 unlink @@ -417,15 +432,10 @@ impl Filesystem for AyaFS { reply.error(EISDIR); return; } - // 删除 dir entry - if let Err(err_code) = self.remove_direntry(&mut parent_inode, entry_index) { - reply.error(err_code); - return; - } - // inode 的 n_links 减 1 + inode.n_links -= 1; if inode.n_links == 0 { - // 释放块的操作在 remove file 里实现了 + // n_links == 0 -> 整个 inode 都要删除掉 match self.remove_file(inode_index) { Ok(flag) => debug!(" unlink {}", flag), Err(err_code) => { @@ -434,8 +444,17 @@ impl Filesystem for AyaFS { } } } else { + // n_links > 0 -> 只是移除了一个 hard link, 将改动写回 self.update_inode(inode_index, inode); } + + // 删除 dir entry + if let Err(err_code) = + self.remove_direntry(parent as usize, &mut parent_inode, name, entry_index) + { + reply.error(err_code); + return; + } reply.ok(); } else { reply.error(ENOENT); @@ -475,7 +494,8 @@ impl Filesystem for AyaFS { let mut parent_inode = parent_inode.clone(); // 不存在 -> No such file or directory - if let Ok((inode_index, entry_index, mut inode)) = self.lookup_name(&parent_inode, name) + if let Ok((inode_index, entry_index, inode)) = + self.lookup_name(parent as usize, &parent_inode, name) { let inode_index = inode_index as usize; // 要删除的 entry 是一般文件, 不用 rmdir @@ -484,11 +504,10 @@ impl Filesystem for AyaFS { return; } - // TODO 检查待删除的 dir 是 empty 的 - - // 删除 dir entry - if let Err(err_code) = self.remove_direntry(&mut parent_inode, entry_index) { - reply.error(err_code); + // 一定有 . 和 .. 所以 size == 2 就是空目录 + // 目录非空 -> ENOTEMPTY + if inode.size > 2 { + reply.error(ENOTEMPTY); return; } @@ -501,6 +520,14 @@ impl Filesystem for AyaFS { } } + // 删除 dir entry + if let Err(err_code) = + self.remove_direntry(parent as usize, &mut parent_inode, name, entry_index) + { + reply.error(err_code); + return; + } + reply.ok(); } else { reply.error(ENOENT); @@ -510,6 +537,10 @@ impl Filesystem for AyaFS { } } + fn open(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + todo!() + } + fn read( &mut self, _req: &Request<'_>, @@ -524,42 +555,111 @@ impl Filesystem for AyaFS { todo!() } - fn opendir(&mut self, _req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { - debug!("OPENDIR ino {:#x} flags {}", ino, flags); - reply.opened(1, 0); + fn write( + &mut self, + _req: &Request<'_>, + ino: u64, + fh: u64, + offset: i64, + data: &[u8], + write_flags: u32, + flags: i32, + lock_owner: Option, + reply: ReplyWrite, + ) { + todo!() + } + + fn release( + &mut self, + _req: &Request<'_>, + _ino: u64, + _fh: u64, + _flags: i32, + _lock_owner: Option, + _flush: bool, + reply: ReplyEmpty, + ) { + todo!() + } + + fn opendir(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { + debug!("opendir(ino: {:#x?}, flags: {:#x?})", ino, flags); + let (access_mask, read, write) = match flags & O_ACCMODE { + O_RDONLY => { + // Behavior is undefined, but most filesystems return EACCES + if flags & libc::O_TRUNC != 0 { + reply.error(libc::EACCES); + return; + } + (R_OK, true, false) + } + O_WRONLY => (W_OK, false, true), + O_RDWR => (R_OK | W_OK, true, true), + _ => { + // flag 非法 + reply.error(EINVAL); + return; + } + }; + + match self.get_inode(ino as usize) { + Some(inode) => { + if !check_access( + req.uid(), + req.gid(), + inode.uid, + inode.gid, + inode.mode, + access_mask, + ) { + reply.error(EACCES); + return; + } + let fd = self.allocate_file_descriptor(ino as usize, read, write); + reply.opened(fd, 0); + } + None => { + reply.error(ENOENT); + } + } } fn readdir( &mut self, req: &Request<'_>, ino: u64, - _fh: u64, // 如果 invoke 它的 opendir 里有指定 fh, 那么这里会收到 fh 参数 + fh: u64, // 如果 invoke 它的 opendir 里有指定 fh, 那么这里会收到 fh 参数 offset: i64, mut reply: ReplyDirectory, ) { + assert_eq!(self.file_handle_map.get(&fh).unwrap().0, ino as usize); if let Some(inode) = self.get_inode(ino as usize) { if inode.is_dir() { if check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) { + debug!("reading dir entries from inode {:#x} with offset {}", ino, offset); let inode = inode.clone(); - let mut entry_index = offset as u32; - while let Some(dir_entry) = self.get_direntry(&inode, entry_index) { - debug!("reading inode {:#x} index {}", ino, entry_index); - if entry_index >= inode.size { - break; - } - // 最开头应该添加 `.` 和 `..`, 但这件事应该在 mkdir 的时候就在 inode 里加好 - // reply.add 返回 true 说明 buffer 已满, 停止累加 + self.load_direntry_map(ino as usize, &inode).unwrap(); + for (entry_index, (name, dir_entry)) in self + .dir_entry_map + .get(&(ino as usize)) + .unwrap() + .iter() + .enumerate() + .skip(offset as usize) + { + debug!(" entry {} from inode {:#x} with name {:?}", entry_index, dir_entry.inode, name); if reply.add( dir_entry.inode as u64, - entry_index as i64, - to_filetype(dir_entry.file_type).expect("file type should not be 0x0!"), - dir_entry.name(), + entry_index as i64 + 1, + to_filetype(dir_entry.file_type).expect("not 0x0!"), + name ) { break; } - entry_index += 1; } + reply.ok(); } else { reply.error(EACCES); @@ -572,11 +672,28 @@ impl Filesystem for AyaFS { } } + fn releasedir( + &mut self, + _req: &Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + reply: ReplyEmpty, + ) { + debug!("releasedir(ino: {:#x?}, fh: {}", ino, fh); + if self.file_handle_map.contains_key(&fh) { + self.file_handle_map.remove(&fh); + reply.ok(); + } else { + reply.error(EBADF); + } + } + fn access(&mut self, req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { // mask: // - 要么是 libc::F_OK (aka 0), 检查文件是否存在 // - 要么是 libc::R_OK,libc::W_OK,libc::X_OK (aka 4/2/1) 构成的 bitmask, 检查是否有对应权限 - debug!("Filesystem::getattr(ino: {}, mask: {})", ino, mask); + debug!("Filesystem::access(ino: {}, mask: {})", ino, mask); if let Some(inode) = self.get_inode(ino as usize) { if mask == libc::F_OK // 只要检查是否存在 -- cgit v1.2.3-70-g09d2