summaryrefslogtreecommitdiff
path: root/src/filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'src/filesystem')
-rw-r--r--src/filesystem/trait_impl.rs207
1 files changed, 162 insertions, 45 deletions
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<u64>,
+ reply: ReplyWrite,
+ ) {
+ todo!()
+ }
+
+ fn release(
+ &mut self,
+ _req: &Request<'_>,
+ _ino: u64,
+ _fh: u64,
+ _flags: i32,
+ _lock_owner: Option<u64>,
+ _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 // 只要检查是否存在