diff options
Diffstat (limited to 'ayafs-core/src/filesystem')
-rw-r--r-- | ayafs-core/src/filesystem/mod.rs | 2 | ||||
-rw-r--r-- | ayafs-core/src/filesystem/trait_impl.rs | 1310 |
2 files changed, 1312 insertions, 0 deletions
diff --git a/ayafs-core/src/filesystem/mod.rs b/ayafs-core/src/filesystem/mod.rs new file mode 100644 index 0000000..1eaa8e8 --- /dev/null +++ b/ayafs-core/src/filesystem/mod.rs @@ -0,0 +1,2 @@ +/// Where upper filesystem calls are implemented. +pub mod trait_impl; diff --git a/ayafs-core/src/filesystem/trait_impl.rs b/ayafs-core/src/filesystem/trait_impl.rs new file mode 100644 index 0000000..b551cf7 --- /dev/null +++ b/ayafs-core/src/filesystem/trait_impl.rs @@ -0,0 +1,1310 @@ +use crate::block_device::BLOCK_SIZE; +use crate::disk::block::DataBlock; +use crate::disk::inode::InodeMode; +use crate::utils::permissions::{check_access, clear_suid_sgid, get_groups}; +use crate::utils::{from_filetype, from_systime, time_now, to_fileattr, to_filetype}; +use crate::{AyaFS, TTL}; +use fuser::TimeOrNow::{Now, SpecificTime}; +use fuser::{ + FileType, Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, + ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, +}; +use libc::{ + c_int, EACCES, EBADF, EEXIST, EINVAL, EIO, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOTDIR, + ENOTEMPTY, EPERM, IPOPT_OFFSET, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY, RENAME_EXCHANGE, + RENAME_NOREPLACE, R_OK, S_ISGID, S_ISUID, S_IXGRP, S_IXOTH, S_IXUSR, W_OK, +}; +use log::debug; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; +use std::path::Path; +use std::slice; +use std::time::SystemTime; + +impl AyaFS {} + +impl Filesystem for AyaFS { + fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { + debug!("`init()"); + Ok(()) + } + + fn destroy(&mut self) { + debug!("destroy()"); + // TODO 写回 + } + + fn lookup(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let parent = parent as usize; + if let Some(parent_inode) = self.get_inode(parent) { + if check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + R_OK, + ) { + let parent_inode = parent_inode.clone(); + 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); + } + Err(err_code) => reply.error(err_code), + }; + } else { + reply.error(EACCES); + } + } else { + reply.error(ENOENT); + } + } + + fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { + if let Some(inode) = self.get_inode(ino as usize) { + reply.attr(&TTL, &to_fileattr(ino as usize, inode)); + } else { + reply.error(ENOENT); + } + } + + fn setattr( + &mut self, + req: &Request<'_>, + ino: u64, + mode: Option<u32>, + uid: Option<u32>, + gid: Option<u32>, + size: Option<u64>, // 当 setattr 被 ftruncate invoke 时会设置 size + atime: Option<TimeOrNow>, + mtime: Option<TimeOrNow>, + _ctime: Option<SystemTime>, + fh: Option<u64>, // 当 setattr 被 ftruncate invoke 时会提供 fh + _crtime: Option<SystemTime>, // 忽略 + _chgtime: Option<SystemTime>, // 忽略 + _bkuptime: Option<SystemTime>, // 忽略 + _flags: Option<u32>, // 忽略 + reply: ReplyAttr, + ) { + if let Some(inode) = self.get_inode_mut(ino as usize) { + // chmod + if let Some(mode) = mode { + debug!("chmod on inode {:#x?} mode {:o}", ino, mode); + if req.uid() != 0 && req.uid() != inode.uid { + reply.error(EPERM); + return; + } // uid == 0 (root) or uid == inode.uid (user itself) + + if req.uid() != 0 + && req.gid() != inode.gid + && !get_groups(req.pid()).contains(&inode.gid) + { + inode.mode = InodeMode((mode & !S_ISGID) as u16); + } else { + inode.mode = InodeMode(mode as u16); + } + + inode.ctime = time_now(); + reply.attr(&TTL, &to_fileattr(ino as usize, inode)); + return; + } + + // chown + if uid.is_some() || gid.is_some() { + debug!("chown on inode {:#x?} uid {:?} gid {:?}", ino, uid, gid); + if let Some(uid) = uid { + // 虽然只有 root 可以 chown 但是 user chown 自己也是不报错的 + if req.uid() != 0 && !(uid == inode.uid && uid == req.uid()) { + reply.error(EPERM); + return; + } + } + if let Some(gid) = gid { + // 只有 root 和 file owner 才可以 chgrp + if req.uid() != 0 && req.uid() != inode.uid { + reply.error(EPERM); + return; + } + // root 可以任意 chgrp, 非 root 只能修改到用户自己所在的组里 + if req.gid() != 0 && !get_groups(req.pid()).contains(&gid) { + reply.error(EPERM); + return; + } + } + + // 可执行文件要清除 suid & sgid + if inode.mode.0 & (S_IXUSR | S_IXGRP | S_IXOTH) as u16 != 0 { + inode.mode = clear_suid_sgid(inode.mode); + } + + // chown 要清除 suid + if let Some(uid) = uid { + inode.uid = uid; + inode.mode.0 &= !S_ISUID as u16; + } + + if let Some(gid) = gid { + inode.gid = gid; + if req.uid() != 0 { + inode.mode.0 &= !S_ISGID as u16; + } + } + inode.ctime = time_now(); + reply.attr(&TTL, &to_fileattr(ino as usize, inode)); + return; + } + + // ftruncate + if let Some(size) = size { + // TODO 当大小减小的时候对应 deallocate 块 + debug!("ftruncate on inode {:#x?} size {:?}", ino, size); + if let Some(file_handle) = fh { + let mut inode = inode.clone(); + let (inode_index, _read, write) = + self.file_handle_map.get(&file_handle).unwrap(); + assert_eq!(ino as usize, *inode_index); + if !write { + reply.error(EACCES); + } else { + inode.size = size as u32; + reply.attr(&TTL, &to_fileattr(*inode_index, &inode)); + self.update_inode(*inode_index, inode); + } + } else { + if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, W_OK) { + reply.error(EACCES); + } else { + inode.size = size as u32; + reply.attr(&TTL, &to_fileattr(ino as usize, &inode)); + } + } + return; + } + + // time 相关 + if atime.is_some() || mtime.is_some() { + let current_time = time_now(); + if let Some(atime) = atime { + debug!("utimensat on inode {:#x?}, atime {:?}", ino, atime); + // root 和 user 可以随意修改 atime, 其他用户只能 touch (即 atime == Now) + if req.uid() != 0 && req.uid() != inode.uid && atime != Now { + reply.error(EPERM); + return; + } + + if req.uid() != 0 + && req.uid() != inode.uid + && check_access( + req.uid(), + req.gid(), + inode.uid, + inode.gid, + inode.mode, + R_OK, // atime 来说是 read TODO 对吗 + ) + { + reply.error(EACCES); + return; + } + + inode.atime = match atime { + SpecificTime(time) => from_systime(time), + Now => current_time, + }; + inode.ctime = current_time; + } + if let Some(mtime) = mtime { + debug!("utimensat on inode {:#x?}, mtime {:?}", ino, mtime); + // root 和 user 可以随意修改 mtime, 其他用户只能 mtime == Now + if req.uid() != 0 && req.uid() != inode.uid && mtime != Now { + reply.error(EPERM); + return; + } + + if req.uid() != 0 + && req.uid() != inode.uid + && check_access( + req.uid(), + req.gid(), + inode.uid, + inode.gid, + inode.mode, + W_OK, // mtime 来说是 write + ) + { + reply.error(EACCES); + return; + } + + inode.mtime = match mtime { + SpecificTime(time) => from_systime(time), + Now => current_time, + }; + inode.ctime = current_time; + } + } + reply.attr(&TTL, &to_fileattr(ino as usize, inode)); + return; + } else { + reply.error(ENOENT); + } + } + + // 这啥语义啊?? + fn readlink(&mut self, req: &Request<'_>, ino: u64, reply: ReplyData) { + debug!("readlink(ino: {})", ino); + if let Some(inode) = self.get_inode(ino as usize) { + if !inode.is_symlink() { + reply.error(ENOENT); + return; + } + if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) { + reply.error(EACCES); + return; + } + let path_length = inode.size as usize; + let mut path = vec![0u8; path_length]; + if path_length < 60 { + debug!( + "symlink path length is {}, reading directly from inode.direct", + path_length + ); + let copy_src = unsafe { + let src = (&inode.direct) as *const u32 as *const u8; + slice::from_raw_parts(src, path_length) + }; + path.as_mut_slice().copy_from_slice(copy_src); + } else { + debug!( + "symlink path length is {}, using original read", + path_length + ); + let inode = inode.clone(); + let mut read_ptr = 0usize; + while read_ptr < path_length { + let block_index = read_ptr / BLOCK_SIZE; + let offset = read_ptr % BLOCK_SIZE; + + let read_length_within_block = if BLOCK_SIZE - offset < path_length - read_ptr { + BLOCK_SIZE - offset + } else { + path_length - read_ptr + }; + + if let Some(block) = self.access_block::<DataBlock>(&inode, block_index) { + (&mut path[read_ptr..read_ptr + read_length_within_block]).copy_from_slice( + &block.block.0[offset..offset + read_length_within_block], + ); + read_ptr += read_length_within_block; + } else { + reply.error(EIO); + return; + } + } + } + debug!( + "readlink path read is {:?}", + OsStr::from_bytes(path.as_slice()) + ); + reply.data(path.as_slice()); + } else { + reply.error(ENOENT); + } + } + + fn mknod( + &mut self, + req: &Request<'_>, + parent: u64, + name: &OsStr, + mode: u32, + _umask: u32, // umask 是用不到的 + _rdev: u32, // the device number (only valid if created file is a device) + reply: ReplyEntry, + ) { + debug!( + "mknod(parent: {}, name: {:?}, mode: {}, umask: {})", + parent, name, mode, _umask + ); + + let parent = parent as usize; + if let Some(parent_inode) = self.get_inode(parent) { + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + + // parent 不是 IFDIR -> Not a directory + if !parent_inode.is_dir() { + reply.error(ENOTDIR); + return; + } + + // 文件名长度超过 255, 返回 filename too long + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let mut parent_inode = parent_inode.clone(); + // 如果已经存在, 返回 already exists + if self.lookup_name(parent, &parent_inode, name).is_ok() { + reply.error(EEXIST); + return; + } + + let mode = mode as u16; + if let Some((child_inode_index, child_inode)) = + self.create_file(mode, req.uid(), req.gid(), 0) + { + let mode = child_inode.mode; + let file_attr = to_fileattr(child_inode_index, child_inode); + if let Err(err_code) = self.add_direntry( + parent, + &mut parent_inode, + child_inode_index, + name, + mode.into(), + ) { + reply.error(err_code); + return; + } + self.update_inode(parent, parent_inode); // 前面 clone 了, 这里写回 + reply.entry(&TTL, &file_attr, 0); + } else { + // create_inode 失败 -> no enough space + reply.error(ENOSPC); + } + } else { + // parent 不存在 -> No such file or directory + reply.error(ENOENT); + } + } + + fn mkdir( + &mut self, + req: &Request<'_>, + parent: u64, + name: &OsStr, + mode: u32, + _umask: u32, // umask 应该也是用不到的 + reply: ReplyEntry, + ) { + if let Some(parent_inode) = self.get_inode(parent as usize) { + // 无权限创建 -> EACCES + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + + // parent 不是 IFDIR -> Not a directory + if !parent_inode.is_dir() { + reply.error(ENOTDIR); + return; + } + + // 文件名长度超过 255 -> filename too long + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let mut parent_inode = parent_inode.clone(); + // 已经存在 -> File exists + 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 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( + parent as usize, + &mut parent_inode, + child_inode_index, + name, + child_inode.mode.into(), + ) { + reply.error(err_code); + return; + } + self.update_inode(parent as usize, parent_inode); // 前面 clone 了, 这里写回 + reply.entry(&TTL, &file_attr, 0); + } else { + // create_inode 失败 -> no enough space + reply.error(ENOSPC); + } + } else { + // parent 不存在 -> No such file or directory + reply.error(ENOENT); + } + } + + fn unlink(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + debug!("unlink(parent: {:#x?}, name: {:?})", parent, name,); + if let Some(parent_inode) = self.get_inode(parent as usize) { + // 无权限删除 -> EACCES + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + + // parent 不是 dir -> ENOTDIR + if !parent_inode.is_dir() { + reply.error(ENOTDIR); + return; + } + + // 文件名长度超过 255 -> filename too long + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + 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 as usize, &parent_inode, name) + { + let inode_index = inode_index as usize; + + match inode.file_type() { + FileType::RegularFile => { + inode.n_links -= 1; + if inode.n_links == 0 { + // n_links == 0 -> 整个 inode 都要删除掉 + match self.remove_file(inode_index) { + Ok(flag) => debug!(" unlink {}", flag), + Err(err_code) => { + reply.error(err_code); + return; + } + } + } else { + // n_links > 0 -> 只是移除了一个 hard link, 将改动写回 + self.update_inode(inode_index, inode); + } + } + FileType::Symlink => {} + FileType::Directory => { + reply.error(EISDIR); + return; + } + _ => { + // Not implemented! + reply.error(EIO); + return; + } + } + + // 删除 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); + } + } else { + reply.error(ENOENT); + } + } + + fn rmdir(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + debug!("rmdir(parent: {:#x?}, name: {:?}", parent, name,); + if let Some(parent_inode) = self.get_inode(parent as usize) { + // 无权限删除 -> EACCES + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + + // parent 不是 dir -> ENOTDIR + if !parent_inode.is_dir() { + reply.error(ENOTDIR); + return; + } + + // 文件名长度超过 255 -> filename too long + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let mut parent_inode = parent_inode.clone(); + // 不存在 -> No such file or directory + 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 + if inode.is_file() { + reply.error(ENOTDIR); + return; + } + + // 一定有 . 和 .. 所以 size == 2 就是空目录 + // 目录非空 -> ENOTEMPTY + if inode.size > 2 { + reply.error(ENOTEMPTY); + return; + } + + // directory 没有 hard link, 删了就是删了 + match self.remove_dir(inode_index) { + Ok(flag) => debug!(" rmdir {}", flag), + Err(err_code) => { + reply.error(err_code); + return; + } + } + + // 删除 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); + } + } else { + reply.error(ENOENT); + } + } + + fn symlink( + &mut self, + req: &Request<'_>, + parent: u64, + link_name: &OsStr, + target: &Path, + reply: ReplyEntry, + ) { + debug!( + "symlink(parent: {}, name: {:?}, target: {:?})", + parent, link_name, target + ); + let parent = parent as usize; + + // let root_inode_index = 1usize; + // if let Some(root_inode) = self.get_inode(root_inode_index) { + // let mut curr_inode_index = root_inode_index; + // let mut curr_inode = root_inode.clone(); + // for segment in target.iter() { + // match self.lookup_name(curr_inode_index, &curr_inode, segment) { + // Ok((next_inode_index, _, next_inode)) => { + // curr_inode_index = next_inode_index as usize; + // curr_inode = next_inode; + // } + // Err(err_code) => { + // reply.error(err_code); + // return; + // } + // } + // } + // } else { + // reply.error(EIO); + // return; + // } + + if let Some(parent_inode) = self.get_inode(parent) { + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + + if !parent_inode.is_dir() { + reply.error(ENOTDIR); + return; + } + + if link_name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let target = target.as_os_str(); + let mut parent_inode = parent_inode.clone(); + if self.lookup_name(parent, &parent_inode, link_name).is_ok() { + reply.error(EEXIST); + return; + } + + if let Some((child_inode_index, child_inode)) = + self.create_symlink(0o777, req.uid(), req.gid(), 0) + { + let mut child_inode = child_inode.clone(); + child_inode.size = target.len() as u32; + if target.len() < 60 { + debug!("create_symlink: target length < 60, allocating in 'direct' section."); + let target_path = target.as_bytes(); + let copy_dst = unsafe { + let dst = (&mut child_inode.direct) as *mut u32 as *mut u8; + slice::from_raw_parts_mut(dst, target_path.len()) + }; + copy_dst.copy_from_slice(target_path); + } else { + debug!("create_symlink: target length >= 60, using original layout."); + let mut write_ptr = 0usize; + while write_ptr < target.len() { + let block_index = write_ptr / BLOCK_SIZE; + let offset = write_ptr % BLOCK_SIZE; + + let write_length_within_block = + if BLOCK_SIZE - offset < target.len() - write_ptr { + BLOCK_SIZE - offset + } else { + target.len() - write_ptr + }; + + if let Some(block) = + self.access_block_mut::<DataBlock>(&child_inode, block_index) + { + block.block.0[offset..offset + write_length_within_block] + .copy_from_slice( + &target.as_bytes() + [write_ptr..write_ptr + write_length_within_block], + ); + write_ptr += write_length_within_block; + } else { + if let Some((_block_index, block_index_within_inode)) = + self.allocate_block_for(&mut child_inode) + { + let block = self + .access_block_mut::<DataBlock>( + &child_inode, + block_index_within_inode, + ) + .unwrap(); + block.block.0[offset..offset + write_length_within_block] + .copy_from_slice( + &target.as_bytes() + [write_ptr..write_ptr + write_length_within_block], + ); + write_ptr += write_length_within_block; + } else { + reply.error(ENOSPC); + return; + } + } + } + } + + let file_attr = to_fileattr(child_inode_index, &child_inode); + self.update_inode(child_inode_index, child_inode); + + if let Err(err_code) = self.add_direntry( + parent, + &mut parent_inode, + child_inode_index, + link_name, + from_filetype(FileType::Symlink), + ) { + reply.error(err_code); + return; + } + self.update_inode(parent, parent_inode); + reply.entry(&TTL, &file_attr, 0); + } else { + reply.error(ENOSPC); + } + } else { + reply.error(ENOENT); + } + } + + fn rename( + &mut self, + req: &Request<'_>, + parent: u64, + name: &OsStr, + new_parent: u64, + new_name: &OsStr, + flags: u32, + reply: ReplyEmpty, + ) { + debug!( + "rename(parent: {}, name: {:?}, new_parent: {}, new_name: {:?})", + parent, name, new_parent, new_name + ); + + let parent = parent as usize; + let new_parent = new_parent as usize; + if let Some(parent_inode) = self.get_inode(parent) { + if !check_access( + req.uid(), + req.gid(), + parent_inode.uid, + parent_inode.gid, + parent_inode.mode, + R_OK, + ) { + reply.error(EACCES); + return; + } + + if name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let mut parent_inode = parent_inode.clone(); + match self.lookup_name(parent, &parent_inode, name) { + Ok((inode_index, entry_index, inode)) => { + if let Some(new_parent_inode) = self.get_inode(new_parent) { + if !check_access( + req.uid(), + req.gid(), + new_parent_inode.uid, + new_parent_inode.gid, + new_parent_inode.mode, + W_OK, + ) { + reply.error(EACCES); + return; + } + if new_name.len() > 255 { + reply.error(ENAMETOOLONG); + return; + } + + let mut new_parent_inode = new_parent_inode.clone(); + match self.lookup_name(new_parent, &new_parent_inode, new_name) { + Ok((new_inode_index, new_entry_index, new_inode)) => { + // 新文件存在 + if flags & RENAME_NOREPLACE != 0 { + // 指定 noreplace 之后不允许覆盖文件 + reply.error(EEXIST); + return; + } + if flags & RENAME_EXCHANGE != 0 { + // 交换两个文件 + if let Err(err_code) = self.exchange_direntry( + parent, + &mut parent_inode, + name, + new_parent, + &mut new_parent_inode, + new_name, + ) { + reply.error(err_code); + return; + } + } else { + // 用新文件替换旧文件 + let dir_entry = self + .get_direntry_by_name(parent, &parent_inode, name) + .unwrap(); + if let Err(err_code) = self.remove_direntry( + parent, + &mut parent_inode, + name, + entry_index, + ) { + reply.error(err_code); + return; + } + if let Err(err_code) = self.remove_direntry( + new_parent, + &mut new_parent_inode, + new_name, + new_entry_index, + ) { + reply.error(err_code); + return; + } + if let Err(err_code) = self.add_direntry_2( + new_parent, + &mut new_parent_inode, + new_name, + dir_entry, + ) { + reply.error(err_code); + return; + } + } + reply.ok(); + } + Err(ENOENT) => { + // 新文件不存在, 删除旧的创建新的 + let dir_entry = self + .get_direntry_by_name(parent, &parent_inode, name) + .unwrap(); + if let Err(err_code) = self.remove_direntry( + parent, + &mut parent_inode, + name, + entry_index, + ) { + reply.error(err_code); + return; + } + if let Err(err_code) = self.add_direntry_2( + new_parent, + &mut new_parent_inode, + new_name, + dir_entry, + ) { + reply.error(err_code); + return; + } + reply.ok(); + } + Err(err_code) => { + // 其他 Err code + reply.error(err_code); + return; + } + } + } else { + reply.error(ENOENT); + } + } + Err(err_code) => { + reply.error(err_code); + return; + } + } + } else { + reply.error(ENOENT); + } + } + + fn open(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { + debug!("open(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(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); + } + } + } + + // read [offset, offset + size) + // - EOF < offset + size -> return EOF - offset + // - EOF > offset + size -> return size + fn read( + &mut self, + req: &Request<'_>, + ino: u64, + fh: u64, + offset: i64, + size: u32, + _flags: i32, + _lock_owner: Option<u64>, // 用不到! + reply: ReplyData, + ) { + 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() { + reply.error(EISDIR); + return; + } + if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) { + reply.error(EACCES); + return; + } + debug!("reading inode {:#x} (offset {} size {})", ino, offset, size); + + if offset as u32 >= inode.size { + // offset 在 EOF 后面, 直接返回一个 0 长度的空 buffer + reply.data(&Vec::new()); + return; + } + + // let read_length = size.min(inode.size.saturating_sub(offset as u32)) as usize; + // 这和下面那个是等同的但是不利于让人看懂…… + let read_length = if offset as u32 + size <= inode.size { + size // 没有越过 EOF, 读取 size 个 byte + } else { + inode.size - offset as u32 // 越过了 EOF, 读取 inode.size - offset 个 byte + } as usize; + + let mut read_buffer = vec![0u8; read_length]; + let mut read_ptr = 0usize; + + let inode = inode.clone(); + + while read_ptr < read_length { + let current_point = offset as usize + read_ptr; + let current_block_index = current_point / BLOCK_SIZE; + let current_offset = current_point % BLOCK_SIZE; + + let read_length_within_block = + if BLOCK_SIZE - current_offset < read_length - read_ptr { + BLOCK_SIZE - current_offset // 可以读到 block 最后 + } else { + read_length - read_ptr // 读到中途会停下来 + }; + + if let Some(block) = self.access_block::<DataBlock>(&inode, current_block_index) { + (&mut read_buffer[read_ptr..read_ptr + read_length_within_block]) + .copy_from_slice( + &block.block.0 + [current_offset..current_offset + read_length_within_block], + ); + read_ptr += read_length_within_block; + } else { + reply.error(EIO); + return; + } + } + + reply.data(&read_buffer); + } + } + + // 写了多少就返回多少 + fn write( + &mut self, + req: &Request<'_>, + ino: u64, + fh: u64, + offset: i64, + data: &[u8], + _write_flags: u32, + _flags: i32, + _lock_owner: Option<u64>, + reply: ReplyWrite, + ) { + 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() { + reply.error(EISDIR); + return; + } + if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, W_OK) { + reply.error(EACCES); + return; + } + debug!( + "writing inode {:#x} (offset {} size {})", + ino, + offset, + data.len() + ); + let write_length = data.len(); + let mut write_ptr = 0usize; + + let mut inode = inode.clone(); + + while write_ptr < write_length { + let current_point = offset as usize + write_ptr; + let current_block_index = current_point / BLOCK_SIZE; + let current_offset = current_point % BLOCK_SIZE; + + let write_length_within_block = + if BLOCK_SIZE - current_offset < write_length - write_ptr { + BLOCK_SIZE - current_offset // 可以写满 block + } else { + write_length - write_ptr // 写完 buffer 就停下来 + }; + + // 当前块已分配, 直接往里写 + if let Some(block) = self.access_block_mut::<DataBlock>(&inode, current_block_index) + { + debug!( + "writing {} bytes in block {} within inode", + write_length_within_block, current_block_index + ); + (block.block.0[current_offset..current_offset + write_length_within_block]) + .copy_from_slice(&data[write_ptr..write_ptr + write_length_within_block]); + write_ptr += write_length_within_block; + } else { + // 当前块未分配,尝试分配 + if let Some((block_index, block_index_within_inode)) = + self.allocate_block_for(&mut inode) + { + debug!("writing {} bytes in allocated block {} within inode, block index is {}", write_length_within_block, block_index_within_inode, block_index); + // 能分配, 往里写 + let block = self + .access_block_mut::<DataBlock>(&inode, block_index_within_inode) + .unwrap(); + (block.block.0[current_offset..current_offset + write_length_within_block]) + .copy_from_slice( + &data[write_ptr..write_ptr + write_length_within_block], + ); + write_ptr += write_length_within_block; + } else { + // 分配不了, 没空间了 + break; + } + } + } + + inode.size = inode.size.max(offset as u32 + write_length as u32); + self.update_inode(ino as usize, inode); + reply.written(write_length as u32); + } else { + reply.error(ENOENT); + } + } + + // fn getxattr( + // &mut self, + // _req: &Request<'_>, + // ino: u64, + // name: &OsStr, + // size: u32, + // reply: ReplyXattr, + // ) { + // todo!() + // } + // + // fn setxattr( + // &mut self, + // _req: &Request<'_>, + // ino: u64, + // name: &OsStr, + // _value: &[u8], + // flags: i32, + // position: u32, + // reply: ReplyEmpty, + // ) { + // todo!() + // } + + fn release( + &mut self, + _req: &Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + _lock_owner: Option<u64>, + _flush: bool, + reply: ReplyEmpty, + ) { + debug!("release(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 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(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 参数 + 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() { + reply.error(ENOTDIR); + return; + } + + if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) { + reply.error(EACCES); + return; + } + + debug!( + "reading dir entries from inode {:#x} with offset {}", + ino, offset + ); + let inode = inode.clone(); + + 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 + 1, + to_filetype(dir_entry.file_type).expect("not 0x0!"), + name, + ) { + break; + } + } + + reply.ok(); + } else { + reply.error(ENOENT); + } + } + + 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 statfs(&mut self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) { + // todo!() + // } + + 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::access(ino: {}, mask: {})", ino, mask); + + if let Some(inode) = self.get_inode(ino as usize) { + if mask == libc::F_OK // 只要检查是否存在 + || check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, mask) + // 需要检查 rwx 权限 + { + reply.ok(); + } else { + reply.error(EACCES); + } + } else { + reply.error(ENOENT); + } + } +} |