use crate::block_device::BLOCK_SIZE; use crate::disk::inode::InodeMode; use crate::utils::make_fileattr; use crate::{utils, AyaFS, TTL}; use fuser::{ FileAttr, FileType, Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyLseek, ReplyWrite, Request, TimeOrNow, }; use libc::{c_int, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR}; use log::debug; use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; use std::time::SystemTime; use users::{get_current_gid, get_current_uid}; impl Filesystem for AyaFS { fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { debug!("Filesystem::init called."); Ok(()) } fn destroy(&mut self) { debug!("Filesystem::destroy()"); } fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { let parent = parent as usize; if let Some(inode) = self.get_inode(parent) { // debug!("{:?}", inode); } // if self.inode_active(parent) { // let (block, offset) = self.locate_inode(parent); // let inode = self.get_inode(block, offset); // debug!("{:?}", inode); // } reply.error(ENOENT); } fn forget(&mut self, _req: &Request<'_>, _ino: u64, _nlookup: u64) { debug!("Filesystem::forget()"); todo!("This is a dumb implementation") } 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) { let attr = FileAttr { ino, size: inode.size as u64, blocks: inode.n_blocks as u64, atime: utils::to_systime(inode.atime), mtime: utils::to_systime(inode.mtime), ctime: utils::to_systime(inode.ctime), crtime: utils::to_systime(inode.crtime), kind: inode.mode.into(), perm: inode.mode.perm(), nlink: inode.n_links as u32, uid: inode.uid, gid: inode.gid, rdev: 0, blksize: BLOCK_SIZE as u32, flags: inode.flags, }; reply.attr(&TTL, &attr); } else { reply.error(ENOENT); } } fn setattr( &mut self, _req: &Request<'_>, ino: u64, mode: Option, uid: Option, gid: Option, size: Option, // TODO 为什么 setattr 还可以设置 size?? atime: Option, mtime: Option, ctime: Option, fh: Option, crtime: Option, _chgtime: Option, _bkuptime: Option, flags: Option, reply: ReplyAttr, ) { if let Some(inode) = self.get_inode_mut(ino as usize) { let mode = mode .and_then(|mode_value| { InodeMode::validate(mode_value as u16) // high 16 bit truncated }) .unwrap_or(inode.mode); inode.mode = mode; // 如果传入的 InodeMode 是合法结果, 那么得到 Some(mode), 否则 None // 需要检查权限吗? 如果需要, 如何知道 invoke 这个 callback 的 UID/GID? let size = size.unwrap_or(inode.size as u64); inode.size = size as u32; // TODO 为什么可以设置 size 呢…… let uid = uid.unwrap_or(inode.uid); let gid = gid.unwrap_or(inode.gid); inode.uid = uid; inode.gid = gid; let (atime, atime_inner) = atime .map(|atime| { let system_time = match atime { TimeOrNow::SpecificTime(system_time) => system_time, TimeOrNow::Now => SystemTime::now(), }; (system_time, utils::from_systime(system_time)) }) .unwrap_or((utils::to_systime(inode.atime), inode.atime)); inode.atime = atime_inner; let (mtime, mtime_inner) = mtime .map(|mtime| { let system_time = match mtime { TimeOrNow::SpecificTime(system_time) => system_time, TimeOrNow::Now => SystemTime::now(), }; (system_time, utils::from_systime(system_time)) }) .unwrap_or((utils::to_systime(inode.mtime), inode.mtime)); inode.mtime = mtime_inner; let (ctime, ctime_inner) = ctime .map(|ctime| (ctime, utils::from_systime(ctime))) .unwrap_or((utils::to_systime(inode.ctime), inode.ctime)); inode.ctime = ctime_inner; let (crtime, crtime_inner) = crtime .map(|crtime| (crtime, utils::from_systime(crtime))) .unwrap_or((utils::to_systime(inode.crtime), inode.crtime)); inode.crtime = crtime_inner; let flags = flags.unwrap_or(inode.flags); inode.flags = flags; let attr = FileAttr { ino, size, blocks: inode.n_blocks as u64, atime, mtime, ctime, crtime, kind: mode.into(), perm: mode.perm(), nlink: inode.n_links as u32, uid, gid, rdev: 0, blksize: BLOCK_SIZE as u32, flags, }; reply.attr(&TTL, &attr); } else { reply.error(ENOSYS); } } fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) { debug!("[Not Implemented] readlink(ino: {})", ino); reply.error(ENOSYS); } 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!( "Filesystem::mknod(parent: {}, name: {:?}, mode: {}, umask: {})", parent, name, mode, _umask ); if let Some(inode) = self.get_inode(parent as usize) { if inode.is_dir() { let name = name.as_bytes(); if name.len() <= 255 { let permissions = (mode & 0o777) as u16; if let Some(inode_index) = self.create_inode( permissions, InodeMode::IFREG, get_current_uid(), get_current_gid(), 0, ) { let inode = self.get_inode(inode_index).unwrap(); let file_attr = make_fileattr(inode_index, inode); // TODO 把 inode 挂到 parent 下 reply.entry(&TTL, &file_attr, 0); } else { // create_inode 失败 -> no enough space reply.error(ENOSPC); } } else { // name 长度超过 255 -> File name too long reply.error(ENAMETOOLONG); } } else { // parent 不是 IFDIR -> Not a directory reply.error(ENOTDIR); } } 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(inode) = self.get_inode(parent as usize) { if inode.is_dir() { let name = name.as_bytes(); if name.len() <= 255 { let permissions = (mode & 0o777) as u16; if let Some(inode_index) = self.create_inode( permissions, InodeMode::IFDIR, get_current_uid(), get_current_gid(), 0, ) { let inode = self.get_inode(inode_index).unwrap(); let file_attr = make_fileattr(inode_index, inode); // TODO 把 inode 挂到 parent 下 reply.entry(&TTL, &file_attr, 0); } else { // create_inode 失败 -> no enough space reply.error(ENOSPC); } } else { // name 长度超过 255 -> File name too long reply.error(ENAMETOOLONG); } } else { // parent 不是 IFDIR -> Not a directory reply.error(ENOTDIR); } } 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(inode) = self.get_inode_mut(parent as usize) { // TODO 找到这个 inode 并且删掉 reply.ok(); } else { reply.error(ENOENT); } } fn read( &mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, _size: u32, _flags: i32, _lock_owner: Option, reply: ReplyData, ) { todo!() } fn readdir( &mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory, ) { if let Some(inode) = self.get_inode(ino as usize) { } else { reply.error(ENOENT); } // if ino != 1 { // reply.error(ENOENT); // return; // } // // let entries = vec![ // (1, FileType::Directory, "."), // (1, FileType::Directory, ".."), // (2, FileType::RegularFile, "hello.txt"), // ]; // // for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) { // // i + 1 means the index of the next entry // if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) { // break; // } // } // reply.ok(); } fn access(&mut self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { debug!("Filesystem::getattr(ino: {}, mask: {})", ino, mask); if let Some(inode) = self.get_inode(ino as usize) { reply.ok() } else { reply.error(ENOENT) } } fn lseek( &mut self, _req: &Request<'_>, ino: u64, fh: u64, offset: i64, whence: i32, reply: ReplyLseek, ) { reply.error(ENOSYS); } }