use crate::block_device::BLOCK_SIZE; use crate::disk::block::DataBlock; use crate::disk::inode::{INODE_PER_BLOCK, 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, ReplyWrite, Request, TimeOrNow, ReplyStatfs, FileAttr}; use libc::{ c_int, EACCES, EBADF, EEXIST, EINVAL, EIO, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY, EPERM, 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()"); self.write_back(); } 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) { debug!( "getattr(ino: {}, incoming_uid: {}, inode_uid: {})", ino, _req.uid(), inode.uid, ); reply.attr(&TTL, &to_fileattr(ino as usize, inode)); } else { reply.error(ENOENT); } } fn setattr( &mut self, req: &Request<'_>, ino: u64, mode: Option, uid: Option, gid: Option, size: Option, // 当 setattr 被 ftruncate invoke 时会设置 size atime: Option, mtime: Option, _ctime: Option, fh: Option, // 当 setattr 被 ftruncate invoke 时会提供 fh _crtime: Option, // 忽略 _chgtime: Option, // 忽略 _bkuptime: Option, // 忽略 _flags: Option, // 忽略 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; 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; 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::(&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); let parent = parent as usize; if let Some(parent_inode) = self.get_inode(parent) { // 无权限删除 -> 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, &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, &mut parent_inode, name, entry_index) { reply.error(err_code); return; } parent_inode.size -= 1; self.update_inode(parent, parent_inode); 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); let parent = parent as usize; if let Some(parent_inode) = self.get_inode(parent) { // 无权限删除 -> 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, &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, &mut parent_inode, name, entry_index) { reply.error(err_code); return; } parent_inode.size -= 1; self.update_inode(parent, parent_inode); 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 u64; 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 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 link(&mut self, req: &Request<'_>, ino: u64, new_parent: u64, new_name: &OsStr, reply: ReplyEntry) { let ino = ino as usize; let new_parent = new_parent as usize; if let Some(parent_inode) = self.get_inode(new_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 new_name.len() > 255 { reply.error(ENAMETOOLONG); return; } let filetype: FileType; let fileattr: FileAttr; let mut parent_inode = parent_inode.clone(); if let Some(inode) = self.get_inode_mut(ino) { inode.n_links += 1; filetype = inode.file_type(); fileattr = to_fileattr(ino, inode); } else { reply.error(ENOENT); return; } if let Err(err_code) = self.add_direntry( new_parent, &mut parent_inode, ino as usize, new_name, from_filetype(filetype) ) { reply.error(err_code); return; } self.update_inode(new_parent, parent_inode); reply.entry(&TTL, &fileattr, 0); } 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, // 用不到! 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 u64 >= 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 size = size as u64; let read_length = if size.checked_add_signed(offset).unwrap() <= inode.size { size // 没有越过 EOF, 读取 size 个 byte } else { inode.size - offset as u64 // 越过了 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::(&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, 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::(&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::(&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 u64 + write_length as u64); self.update_inode(ino as usize, inode); reply.written(write_length as u32); } else { reply.error(ENOENT); } } fn release( &mut self, _req: &Request<'_>, ino: u64, fh: u64, _flags: i32, _lock_owner: Option, _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) { if let Some(_) = self.get_inode(ino as usize) { self.super_block.used_inode_number = self.inode_bitmap.count; self.super_block.used_block_number = self.data_bitmap.count; reply.statfs( self.super_block.used_block_number, self.super_block.data_block_number - self.super_block.used_block_number, self.super_block.data_block_number - self.super_block.used_block_number, self.super_block.used_inode_number, self.super_block.inode_block_number * INODE_PER_BLOCK as u64 - self.super_block.used_inode_number, BLOCK_SIZE as u32, 255, BLOCK_SIZE as u32, ); } else { reply.error(ENOENT); } } 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) { debug!(" uid: {}, gid: {}, incoming_uid: {}, incoming_gid: {}, mask: {}", inode.uid, inode.gid, req.uid(), req.gid(), mask); 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); } } }