summaryrefslogtreecommitdiff
path: root/src/filesystem/trait_impl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/filesystem/trait_impl.rs')
-rw-r--r--src/filesystem/trait_impl.rs258
1 files changed, 223 insertions, 35 deletions
diff --git a/src/filesystem/trait_impl.rs b/src/filesystem/trait_impl.rs
index 78916ed..5523c8c 100644
--- a/src/filesystem/trait_impl.rs
+++ b/src/filesystem/trait_impl.rs
@@ -1,15 +1,16 @@
use crate::disk::inode::InodeMode;
use crate::utils::permissions::get_groups;
use crate::utils::permissions::{check_access, clear_suid_sgid};
-use crate::utils::{from_systime, time_now, to_fileattr, to_filetype};
+use crate::utils::{from_filetype, from_systime, time_now, to_fileattr, to_filetype};
use crate::{AyaFS, TTL};
use fuser::TimeOrNow::{Now, SpecificTime};
-use fuser::{
- Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyWrite, Request, TimeOrNow,
-};
-use libc::{c_int, EACCES, EBADF, EEXIST, EINVAL, EIO, 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 fuser::{Filesystem, FileType, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyWrite, Request, TimeOrNow};
+use libc::{c_int, EACCES, EBADF, EEXIST, EFAULT, EINVAL, EIO, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR, ENOTEMPTY, EPERM, IPOPT_OFFSET, link, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY, R_OK, S_ISGID, S_ISUID, S_IXGRP, S_IXOTH, S_IXUSR, W_OK, write};
use log::debug;
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
+use std::os::unix::ffi::OsStrExt;
+use std::path::Path;
+use std::slice;
use std::time::SystemTime;
use crate::block_device::BLOCK_SIZE;
use crate::disk::block::DataBlock;
@@ -155,6 +156,7 @@ impl Filesystem for AyaFS {
// 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();
@@ -254,9 +256,184 @@ impl Filesystem for AyaFS {
}
}
- fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) {
- debug!("[Not Implemented] readlink(ino: {})", ino);
- reply.error(ENOSYS);
+ 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 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(
@@ -270,11 +447,12 @@ impl Filesystem for AyaFS {
reply: ReplyEntry,
) {
debug!(
- "Filesystem::mknod(parent: {}, name: {:?}, mode: {}, umask: {})",
+ "mknod(parent: {}, name: {:?}, mode: {}, umask: {})",
parent, name, mode, _umask
);
- if let Some(parent_inode) = self.get_inode(parent as usize) {
+ let parent = parent as usize;
+ if let Some(parent_inode) = self.get_inode(parent) {
if !check_access(
req.uid(),
req.gid(),
@@ -302,7 +480,7 @@ impl Filesystem for AyaFS {
let mut parent_inode = parent_inode.clone();
// 如果已经存在, 返回 already exists
if self
- .lookup_name(parent as usize, &parent_inode, name)
+ .lookup_name(parent, &parent_inode, name)
.is_ok()
{
reply.error(EEXIST);
@@ -313,19 +491,19 @@ impl Filesystem for AyaFS {
if let Some((child_inode_index, child_inode)) =
self.create_file(mode, req.uid(), req.gid(), 0)
{
- let child_inode = child_inode.clone();
- let file_attr = to_fileattr(child_inode_index, &child_inode);
+ let mode = child_inode.mode;
+ let file_attr = to_fileattr(child_inode_index, child_inode);
if let Err(err_code) = self.add_direntry(
- parent as usize,
+ parent,
&mut parent_inode,
child_inode_index,
name,
- child_inode.mode.into(),
+ mode.into(),
) {
reply.error(err_code);
return;
}
- self.update_inode(parent as usize, parent_inode); // 前面 clone 了, 这里写回
+ self.update_inode(parent, parent_inode); // 前面 clone 了, 这里写回
reply.entry(&TTL, &file_attr, 0);
} else {
// create_inode 失败 -> no enough space
@@ -444,27 +622,37 @@ impl Filesystem for AyaFS {
self.lookup_name(parent as usize, &parent_inode, name)
{
let inode_index = inode_index as usize;
- // 要删除的 entry 是目录, 不能用 unlink
- if inode.is_dir() {
- reply.error(EISDIR);
- return;
- }
-
- 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;
+
+ 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;
}
- } 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)