summaryrefslogtreecommitdiff
path: root/ayafs-core/src/filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'ayafs-core/src/filesystem')
-rw-r--r--ayafs-core/src/filesystem/mod.rs2
-rw-r--r--ayafs-core/src/filesystem/trait_impl.rs1310
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);
+ }
+ }
+}