From 7af0771f9a3031acc36a6990d07bdb92a61c0c75 Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Wed, 29 Nov 2023 03:29:34 -0800 Subject: symlink, probably not working --- src/filesystem/trait_impl.rs | 258 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 223 insertions(+), 35 deletions(-) (limited to 'src/filesystem/trait_impl.rs') 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::(&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::(&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::(&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) -- cgit v1.2.3-70-g09d2