summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/disk/allocation.rs2
-rw-r--r--src/disk/block.rs119
-rw-r--r--src/disk/inode.rs41
-rw-r--r--src/filesystem/trait_impl.rs518
-rw-r--r--src/main.rs14
-rw-r--r--src/memory/cached_block.rs85
-rw-r--r--src/memory/cached_inode.rs71
-rw-r--r--src/memory/inode_iter.rs233
-rw-r--r--src/memory/mod.rs1
-rw-r--r--src/utils/constants.rs1
-rw-r--r--src/utils/mod.rs61
-rw-r--r--src/utils/permissions.rs62
12 files changed, 865 insertions, 343 deletions
diff --git a/src/disk/allocation.rs b/src/disk/allocation.rs
index 5643dd3..bde2014 100644
--- a/src/disk/allocation.rs
+++ b/src/disk/allocation.rs
@@ -1,6 +1,6 @@
use crate::disk::block::{DataBlock, DoubleIndirectBlock, IndirectBlock, TripleIndirectBlock};
use crate::disk::inode::Inode;
-use crate::memory::cached_block::{convert, convert_mut};
+use crate::memory::cached_block::convert;
use crate::AyaFS;
impl AyaFS {
diff --git a/src/disk/block.rs b/src/disk/block.rs
index d287ee6..0058b3e 100644
--- a/src/disk/block.rs
+++ b/src/disk/block.rs
@@ -1,7 +1,10 @@
use crate::disk::inode::Inode;
+use std::ffi::{OsStr, OsString};
+use std::os::unix::ffi::OsStrExt;
pub trait Block: Default + Clone {}
+#[repr(C)]
#[derive(Clone)]
pub struct DataBlock(pub(crate) [u8; 4096]);
@@ -13,6 +16,7 @@ impl Default for DataBlock {
impl Block for DataBlock {}
+#[repr(C)]
#[derive(Clone)]
pub struct InodeBlock {
pub(crate) inodes: [Inode; 16],
@@ -47,53 +51,70 @@ impl Block for InodeBlock {}
const FULL_MAP: u32 = 0b111_111_111_111_111;
+#[repr(C)]
#[derive(Clone)]
-pub struct DirectoryBlock {
- entries: [[u8; 256]; 15],
- inode_ids: [usize; 15],
- occupancy_map: u32,
- reserved: [u8; 132],
+pub struct DirectoryEntry {
+ pub inode: u32,
+ pub record_len: u16,
+ pub name_len: u8,
+ pub file_type: u8,
+ pub name: [u8; 256],
}
-impl Default for DirectoryBlock {
+impl DirectoryEntry {
+ pub(crate) fn name(&self) -> OsString {
+ let name = &self.name[0..self.name_len as usize];
+ OsStr::from_bytes(name).to_os_string()
+ }
+}
+
+impl Default for DirectoryEntry {
fn default() -> Self {
Self {
- entries: [[0; 256]; 15],
- inode_ids: [0; 15],
- occupancy_map: 0,
- reserved: [0xFF; 132],
+ inode: 0,
+ record_len: 0,
+ name_len: 0,
+ file_type: 0x0,
+ name: [0; 256],
}
}
}
-impl Block for DirectoryBlock {}
-
-impl DirectoryBlock {
- fn vacant(&self) -> bool {
- self.occupancy_map & FULL_MAP != FULL_MAP
- }
-
- fn first_free(&self) -> Option<usize> {
- todo!()
- }
-
- fn mark_busy(&mut self, entry_id: usize) {
- todo!()
- }
+#[repr(C)]
+#[derive(Clone)]
+pub struct DirectoryBlock {
+ pub entries: [DirectoryEntry; 15],
+ reserved: [u8; 136],
+}
- /// 需要判断 entry_name.len() <= 255
- pub fn write_entry(&mut self, entry_name: &[u8], entry_inode_id: usize) -> Option<usize> {
- if let Some(entry_id) = self.first_free() {
- self.mark_busy(entry_id);
- self.entries[entry_id].copy_from_slice(entry_name);
- self.inode_ids[entry_id] = entry_inode_id;
- Some(entry_id)
- } else {
- None
+impl Default for DirectoryBlock {
+ fn default() -> Self {
+ Self {
+ entries: [
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ DirectoryEntry::default(),
+ ],
+ reserved: [0xFF; 136],
}
}
}
+impl Block for DirectoryBlock {}
+
+#[repr(C)]
#[derive(Clone)]
pub struct IndirectBlock {
pub entries: [u32; 1024],
@@ -107,16 +128,7 @@ impl Default for IndirectBlock {
impl Block for IndirectBlock {}
-impl IndirectBlock {
- pub fn full(&self) -> bool {
- todo!()
- }
-
- pub fn allocate(&mut self) -> Option<usize> {
- todo!()
- }
-}
-
+#[repr(C)]
#[derive(Clone)]
pub struct DoubleIndirectBlock {
pub indirect: [u32; 1024],
@@ -132,16 +144,7 @@ impl Default for DoubleIndirectBlock {
impl Block for DoubleIndirectBlock {}
-impl DoubleIndirectBlock {
- pub fn full(&self) -> bool {
- todo!()
- }
-
- pub fn allocate(&mut self) -> Option<usize> {
- todo!()
- }
-}
-
+#[repr(C)]
#[derive(Clone)]
pub struct TripleIndirectBlock {
pub double_indirect: [u32; 1024],
@@ -156,13 +159,3 @@ impl Default for TripleIndirectBlock {
}
impl Block for TripleIndirectBlock {}
-
-impl TripleIndirectBlock {
- pub fn full(&self) -> bool {
- todo!()
- }
-
- pub fn allocate(&mut self) -> Option<usize> {
- todo!()
- }
-}
diff --git a/src/disk/inode.rs b/src/disk/inode.rs
index 1095aca..256d2f5 100644
--- a/src/disk/inode.rs
+++ b/src/disk/inode.rs
@@ -1,11 +1,11 @@
use bitflags::bitflags;
use fuser::{FileAttr, FileType};
-use std::fs::File;
+use crate::utils;
-const DIRECT_NUMBER: usize = 15;
+pub const DIRECT_NUMBER: usize = 15;
#[derive(Debug, Clone, Copy)]
-pub struct InodeMode(u16);
+pub struct InodeMode(pub u16);
bitflags! {
impl InodeMode: u16 {
@@ -33,6 +33,34 @@ bitflags! {
}
impl InodeMode {
+ pub(crate) fn exec_other(&self) -> bool {
+ self.0 & Self::IXOTH.0 != 0
+ }
+ pub(crate) fn write_other(&self) -> bool {
+ self.0 & Self::IWOTH.0 != 0
+ }
+ pub(crate) fn read_other(&self) -> bool {
+ self.0 & Self::IROTH.0 != 0
+ }
+ pub(crate) fn exec_group(&self) -> bool {
+ self.0 & Self::IXGRP.0 != 0
+ }
+ pub(crate) fn write_group(&self) -> bool {
+ self.0 & Self::IWGRP.0 != 0
+ }
+ pub(crate) fn read_group(&self) -> bool {
+ self.0 & Self::IRGRP.0 != 0
+ }
+ pub(crate) fn exec_user(&self) -> bool {
+ self.0 & Self::IXUSR.0 != 0
+ }
+ pub(crate) fn write_user(&self) -> bool {
+ self.0 & Self::IWUSR.0 != 0
+ }
+ pub(crate) fn read_user(&self) -> bool {
+ self.0 & Self::IRUSR.0 != 0
+ }
+
pub(crate) fn perm(&self) -> u16 {
self.0 & 0x0FFF
}
@@ -69,6 +97,13 @@ impl From<InodeMode> for FileType {
}
}
+impl From<InodeMode> for u8 {
+ fn from(value: InodeMode) -> Self {
+ utils::from_filetype(value.into())
+ }
+}
+
+
/// Pretty much the same with ext2, with minor changes:
/// - removed OS dependent attributes (osd1 & osd2)
/// - removed i_faddr since fragmentation is not supported
diff --git a/src/filesystem/trait_impl.rs b/src/filesystem/trait_impl.rs
index a25a383..c533c41 100644
--- a/src/filesystem/trait_impl.rs
+++ b/src/filesystem/trait_impl.rs
@@ -1,67 +1,69 @@
-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 crate::utils::{from_systime, time_now, to_fileattr, to_filetype};
+use crate::utils::permissions::get_groups;
+use crate::{AyaFS, TTL};
+use fuser::{Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty,
+ ReplyEntry, ReplyLseek, ReplyOpen, Request, TimeOrNow,
};
-use libc::{c_int, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR};
+use libc::{c_int, EACCES, EEXIST, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR, EPERM, 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::time::SystemTime;
-use users::{get_current_gid, get_current_uid};
+use fuser::TimeOrNow::{Now, SpecificTime};
+use crate::utils::permissions::{check_access, clear_suid_sgid};
+
+impl AyaFS {
+
+}
impl Filesystem for AyaFS {
fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> {
- debug!("Filesystem::init called.");
+ debug!("`init()");
Ok(())
}
fn destroy(&mut self) {
- debug!("Filesystem::destroy()");
+ debug!("destroy()");
+ // TODO 写回
}
- 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);
+ fn lookup(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
+ if name.len() > 255 {
+ reply.error(ENAMETOOLONG);
+ return;
}
- // 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")
+ 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_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) {
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);
+ reply.attr(&TTL, &to_fileattr(ino as usize, inode));
} else {
reply.error(ENOENT);
}
@@ -69,96 +71,159 @@ impl Filesystem for AyaFS {
fn setattr(
&mut self,
- _req: &Request<'_>,
+ req: &Request<'_>,
ino: u64,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
- size: Option<u64>, // TODO 为什么 setattr 还可以设置 size??
+ size: Option<u64>, // TODO 当 setattr 被 ftruncate invoke 时会设置 size
atime: Option<TimeOrNow>,
mtime: Option<TimeOrNow>,
- ctime: Option<SystemTime>,
- fh: Option<u64>,
- crtime: Option<SystemTime>,
- _chgtime: Option<SystemTime>,
- _bkuptime: Option<SystemTime>,
- flags: Option<u32>,
+ _ctime: Option<SystemTime>,
+ fh: Option<u64>, // TODO 当 setattr 被 ftruncate invoke 时会提供 fh
+ _crtime: Option<SystemTime>, // 忽略
+ _chgtime: Option<SystemTime>, // 忽略
+ _bkuptime: Option<SystemTime>, // 忽略
+ _flags: Option<u32>, // 忽略
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(),
+ // 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 {
+ debug!("ftruncate on inode {:#x?} size {:?}", ino, size);
+ if let Some(file_handle) = fh {
+ // 有 file handle 的
+ todo!()
+ } else {
+ // 没有 file handle 基于 inode 的
+ todo!()
+ }
+ }
+
+ // 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,
};
- (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(),
+ 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,
};
- (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);
+ inode.ctime = current_time;
+ }
+ }
+ reply.attr(&TTL, &to_fileattr(ino as usize, inode));
+ return;
} else {
- reply.error(ENOSYS);
+ reply.error(ENOENT);
}
}
@@ -169,7 +234,7 @@ impl Filesystem for AyaFS {
fn mknod(
&mut self,
- _req: &Request<'_>,
+ req: &Request<'_>,
parent: u64,
name: &OsStr,
mode: u32,
@@ -181,33 +246,51 @@ impl Filesystem for AyaFS {
"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(),
+
+ if let Some(parent_inode) = self.get_inode(parent as usize) {
+ if check_access(
+ req.uid(),
+ req.gid(),
+ parent_inode.uid,
+ parent_inode.gid,
+ parent_inode.mode,
+ W_OK
+ ) {
+ if parent_inode.is_dir() {
+ let parent_inode = parent_inode.clone();
+ // 如果已经存在, 返回 already exists
+ if self.lookup_name(&parent_inode, name).is_ok() {
+ reply.error(EEXIST);
+ return;
+ }
+ // 文件名长度超过 255, 返回 filename too long
+ if name.len() > 255 {
+ reply.error(ENAMETOOLONG);
+ return;
+ }
+
+ let mode = mode as u16;
+ if let Some((inode_index, inode)) = self.create_file(
+ mode,
+ req.uid(),
+ req.gid(),
0,
) {
- let inode = self.get_inode(inode_index).unwrap();
- let file_attr = make_fileattr(inode_index, inode);
+ let file_attr = to_fileattr(inode_index, inode);
// TODO 把 inode 挂到 parent 下
+ 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 {
- // name 长度超过 255 -> File name too long
- reply.error(ENAMETOOLONG);
+ // parent 不是 IFDIR -> Not a directory
+ reply.error(ENOTDIR);
}
} else {
- // parent 不是 IFDIR -> Not a directory
- reply.error(ENOTDIR);
+ reply.error(EACCES);
}
} else {
// parent 不存在 -> No such file or directory
@@ -217,40 +300,57 @@ impl Filesystem for AyaFS {
fn mkdir(
&mut self,
- _req: &Request<'_>,
+ 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(),
+ if let Some(parent_inode) = self.get_inode(parent as usize) {
+ if check_access(
+ req.uid(),
+ req.gid(),
+ parent_inode.uid,
+ parent_inode.gid,
+ parent_inode.mode,
+ W_OK
+ ) {
+ if parent_inode.is_dir() {
+ let parent_inode = parent_inode.clone();
+ // 如果已经存在, 返回 already exists
+ if self.lookup_name(&parent_inode, name).is_ok() {
+ reply.error(EEXIST);
+ return;
+ }
+ // 文件名长度超过 255, 返回 filename too long
+ if name.len() > 255 {
+ reply.error(ENAMETOOLONG);
+ return;
+ }
+
+ let mode = mode as u16;
+ if let Some((inode_index, inode)) = self.create_directory(
+ mode,
+ req.uid(),
+ req.gid(),
0,
) {
- let inode = self.get_inode(inode_index).unwrap();
- let file_attr = make_fileattr(inode_index, inode);
+ let file_attr = to_fileattr(inode_index, inode);
// TODO 把 inode 挂到 parent 下
+ 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 {
- // name 长度超过 255 -> File name too long
- reply.error(ENAMETOOLONG);
+ // parent 不是 IFDIR -> Not a directory
+ reply.error(ENOTDIR);
}
} else {
- // parent 不是 IFDIR -> Not a directory
- reply.error(ENOTDIR);
+ reply.error(EACCES);
}
} else {
// parent 不存在 -> No such file or directory
@@ -259,13 +359,28 @@ impl Filesystem for AyaFS {
}
fn unlink(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
- debug!(
- "unlink(parent: {:#x?}, name: {:?})",
- parent, name,
- );
+ debug!("unlink(parent: {:#x?}, name: {:?})", parent, name,);
if let Some(inode) = self.get_inode_mut(parent as usize) {
- // TODO 找到这个 inode 并且删掉
- reply.ok();
+ if inode.is_file() {
+ // TODO 找到这个 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,);
+ if let Some(inode) = self.get_inode_mut(parent as usize) {
+ if inode.is_file() {
+ // TODO 找到这个 inode 并且删掉
+ reply.ok();
+ } else {
+ reply.error(ENOTDIR);
+ }
} else {
reply.error(ENOENT);
}
@@ -285,45 +400,78 @@ 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 readdir(
&mut self,
- _req: &Request<'_>,
+ req: &Request<'_>,
ino: u64,
- _fh: u64,
+ _fh: u64, // 如果 invoke 它的 opendir 里有指定 fh, 那么这里会收到 fh 参数
offset: i64,
mut reply: ReplyDirectory,
) {
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,
+ ) {
+ 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 已满, 停止累加
+ 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(),
+ ) {
+ break;
+ }
+ entry_index += 1;
+ }
+ reply.ok();
+ } else {
+ reply.error(EACCES);
+ }
+ } else {
+ reply.error(ENOTDIR);
+ }
} 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) {
+ 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);
+
if let Some(inode) = self.get_inode(ino as usize) {
- reply.ok()
+ 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)
+ reply.error(ENOENT);
}
}
diff --git a/src/main.rs b/src/main.rs
index 2df0a10..e0bfa2e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,7 +30,7 @@ struct Args {
allow_root: bool,
}
-const TTL: Duration = Duration::from_secs(1);
+const TTL: Duration = Duration::new(0, 0);
const INODE_PER_BLOCK: usize = BLOCK_SIZE / INODE_SIZE;
/// The design of MyFS is rather simple:
@@ -111,17 +111,11 @@ impl AyaFS {
+ inode_bitmap_block_number
+ inode_block_number
+ 1,
- cached_inodes: BlockCache::new(device.clone(), 256),
- cached_blocks: BlockCache::new(device.clone(), 256),
+ cached_inodes: BlockCache::new(device.clone(), 1024),
+ cached_blocks: BlockCache::new(device.clone(), 8192),
};
- fs.create_inode(
- 0o755,
- InodeMode::IFDIR,
- get_current_uid(),
- get_current_gid(),
- 0,
- );
+ fs.create_directory(0o755, get_current_uid(), get_current_gid(), 0);
fs
}
diff --git a/src/memory/cached_block.rs b/src/memory/cached_block.rs
index eb59a4b..77cdf61 100644
--- a/src/memory/cached_block.rs
+++ b/src/memory/cached_block.rs
@@ -1,5 +1,6 @@
use crate::block_device::{BlockDevice, BLOCK_SIZE};
use crate::disk::block::Block;
+use crate::disk::inode::Inode;
use crate::AyaFS;
use and_then_some::BoolExt;
use log::debug;
@@ -70,17 +71,18 @@ impl<T: Block> BlockCache<T> {
/// 从 LRU cache 里获取一个 block 的引用, 如果没有在 cache 中会加载.
pub(crate) fn get_block<U: Block>(&mut self, index: usize) -> Option<&CachedBlock<U>> {
- self.load_block(index)
- .and_then(|| self.cache.get(&index).map(convert::<T, U>))
+ if !self.cache.contains(&index) {
+ self.load_block(index);
+ }
+ self.cache.get(&index).map(convert::<T,U>)
}
/// 从 LRU cache 里获取一个 block 的可变引用, 如果没有在 cache 中会加载.
pub(crate) fn get_block_mut<U: Block>(&mut self, index: usize) -> Option<&mut CachedBlock<U>> {
- debug!("Blockcache get block mut");
- self.load_block(index).and_then(|| {
- debug!("block loaded");
- self.cache.get_mut(&index).map(convert_mut::<T, U>)
- })
+ if !self.cache.contains(&index) {
+ self.load_block(index);
+ }
+ self.cache.get_mut(&index).map(convert_mut::<T, U>)
}
/// 从 LRU cache 中读取一个 block 的引用, *不会* 影响 LRU cache 的结构, 如果没有在 cache 中不会加载.
@@ -145,73 +147,4 @@ impl AyaFS {
pub(crate) fn update_block<T: Block>(&mut self, block: CachedBlock<T>) -> bool {
self.cached_blocks.update_block(block)
}
-
- // pub(crate) fn update_block<T: Block>(&mut self, block: CachedBlock<T>) -> bool {
- // if self.cached_blocks.contains(&block.index) {
- // let data_block = convert::<T, DataBlock>(&block).clone();
- // self.cached_blocks.push(block.index, data_block);
- // true
- // } else {
- // false
- // }
- // }
- //
- // /// 从 LRU cache 里获取一个 block 的引用, 如果没有在 cache 中会加载.
- // pub(crate) fn get_block<T: Block>(&mut self, index: usize) -> Option<&CachedBlock<T>> {
- // self.load_block(index)
- // .and_then(|| self.cached_blocks.get(&index).map(convert::<DataBlock, T>))
- // }
- //
- // /// 从 LRU cache 里获取一个 block 的可变引用, 如果没有在 cache 中会加载.
- // pub(crate) fn get_block_mut<T: Block>(&mut self, index: usize) -> Option<&mut CachedBlock<T>> {
- // self.load_block(index).and_then(|| {
- // self.cached_blocks
- // .get_mut(&index)
- // .map(convert_mut::<DataBlock, T>)
- // })
- // }
- //
- // /// 从 LRU cache 中读取一个 block 的引用, *不会* 影响 LRU cache 的结构, 如果没有在 cache 中不会加载.
- // pub(crate) fn peek_block<T: Block>(&self, index: usize) -> Option<&CachedBlock<T>> {
- // self.cached_blocks.peek(&index).map(convert::<DataBlock, T>)
- // }
- //
- // /// 从 LRU cache 中读取一个 block 的可变引用, *不会* 影响 LRU cache 的结构, 如果没有在 cache 中不会加载.
- // pub(crate) fn peek_block_mut<T: Block>(&mut self, index: usize) -> Option<&mut CachedBlock<T>> {
- // self.cached_blocks.peek_mut(&index).map(convert_mut::<DataBlock, T>)
- // }
- //
- // pub(crate) fn load_block(&mut self, index: usize) -> bool {
- // // 先看这个 block 是不是 valid, 不 valid 直接返回 false.
- // if !self.data_bitmap.query(index) {
- // self.cached_blocks.pop(&index);
- // // deallocate 时只更新 bitmap 没有动 cache, lazy 地驱逐 cache 中的无效 entry.
- // return false;
- // }
- //
- // // 接下来这个 block 一定 valid. 如果 cache 里没有这个 block 就把它 load 到 cache 里
- // if self.cached_blocks.contains(&index) == false {
- // let block = DataBlock::default();
- // let buffer = unsafe {
- // std::slice::from_raw_parts_mut(&block as *const DataBlock as *mut u8, BLOCK_SIZE)
- // };
- // self.device.read(index, buffer);
- // let cached_block = CachedBlock {
- // block,
- // index,
- // dirty: false,
- // };
- // if let Some((old_index, old_block)) = self.cached_blocks.push(index, cached_block) {
- // assert_ne!(old_index, index); // 只有 block 不在 cache 里的时候才会插入
- // if old_block.dirty {
- // let old_block_ptr = &old_block.block as *const DataBlock as *mut u8;
- // let old_block_buffer =
- // unsafe { std::slice::from_raw_parts(old_block_ptr, BLOCK_SIZE) };
- // self.device.write(old_index, old_block_buffer);
- // }
- // }
- // }
- //
- // true
- // }
}
diff --git a/src/memory/cached_inode.rs b/src/memory/cached_inode.rs
index dd5e5c3..be87850 100644
--- a/src/memory/cached_inode.rs
+++ b/src/memory/cached_inode.rs
@@ -1,10 +1,76 @@
-use crate::disk::block::InodeBlock;
+use crate::disk::block::{DirectoryBlock, DirectoryEntry, InodeBlock};
use crate::disk::inode::{Inode, InodeMode, INODE_SIZE};
+use crate::utils::from_filetype;
use crate::{utils, AyaFS};
use and_then_some::BoolExt;
+use fuser::FileType;
use log::debug;
impl AyaFS {
+ pub(crate) fn create_file(
+ &mut self,
+ permissions: u16,
+ uid: u32,
+ gid: u32,
+ flags: u32,
+ ) -> Option<(usize, &Inode)> {
+ self.inode_bitmap.allocate().map(|inode_index| {
+ self.get_inode_mut(inode_index).map(|inode| {
+ *inode = Inode::file(permissions, uid, gid, utils::time_now(), flags, 0, 0, 0);
+ });
+ (inode_index, self.get_inode(inode_index).unwrap())
+ })
+ }
+
+ pub(crate) fn create_directory(
+ &mut self,
+ permissions: u16,
+ uid: u32,
+ gid: u32,
+ flags: u32,
+ ) -> Option<(usize, &Inode)> {
+ self.inode_bitmap.allocate().map(|inode_index| {
+ // 创建 Inode
+ let mut new_inode =
+ Inode::directory(permissions, uid, gid, utils::time_now(), flags, 0, 0, 0);
+ // 分配第一个 direct block
+ new_inode.direct[0] = self.allocate_block_for(&mut new_inode).unwrap();
+ new_inode.size = 2;
+ // 在 direct block 里分配 . 和 ..
+ if let Some(directory_block) =
+ self.get_block_mut::<DirectoryBlock>(new_inode.direct[0] as usize)
+ {
+ let dot = '.'.to_ascii_lowercase() as u8;
+ // add dot entry
+ directory_block.block.entries[0] = DirectoryEntry {
+ inode: inode_index as u32,
+ record_len: 264,
+ name_len: 1,
+ file_type: 0x2,
+ name: [0; 256],
+ };
+ directory_block.block.entries[0].name[0] = dot;
+
+ // add dot dot entry
+ directory_block.block.entries[1] = DirectoryEntry {
+ inode: 0, // TODO set this as parent inode number
+ record_len: 264,
+ name_len: 2,
+ file_type: 0x2,
+ name: [0; 256],
+ };
+ directory_block.block.entries[1].name[0] = dot;
+ directory_block.block.entries[1].name[1] = dot;
+ }
+ // 把 inode 放到指定位置
+ self.get_inode_mut(inode_index).map(|inode| {
+ *inode = new_inode;
+ });
+
+ (inode_index, self.get_inode(inode_index).unwrap())
+ })
+ }
+
pub(crate) fn create_inode(
&mut self,
permissions: u16,
@@ -13,9 +79,7 @@ impl AyaFS {
gid: u32,
flags: u32,
) -> Option<usize> {
- debug!("create inode");
self.inode_bitmap.allocate().map(|inode_index| {
- debug!("creating inode");
self.get_inode_mut(inode_index).map(|inode| {
*inode = Inode::make_inode(
permissions,
@@ -29,7 +93,6 @@ impl AyaFS {
0,
);
});
- debug!("inode created");
inode_index
})
}
diff --git a/src/memory/inode_iter.rs b/src/memory/inode_iter.rs
new file mode 100644
index 0000000..d5909bc
--- /dev/null
+++ b/src/memory/inode_iter.rs
@@ -0,0 +1,233 @@
+use std::ffi::OsStr;
+use std::os::unix::ffi::OsStrExt;
+use libc::{c_int, ENOENT, ENOSPC};
+use crate::disk::block::{Block, DirectoryBlock, DirectoryEntry, DoubleIndirectBlock, IndirectBlock, TripleIndirectBlock};
+use crate::disk::inode::{Inode, DIRECT_NUMBER};
+use crate::{AyaFS, INODE_PER_BLOCK};
+use crate::memory::cached_block::CachedBlock;
+
+impl AyaFS {
+ pub(crate) fn access_block<T: Block>(&mut self, inode: &Inode, mut block_index_within_inode: usize) -> Option<&CachedBlock<T>> {
+ // direct block
+ if block_index_within_inode < DIRECT_NUMBER {
+ let block_index = inode.direct[block_index_within_inode] as usize;
+ return self.get_block::<T>(block_index);
+ } else {
+ block_index_within_inode -= DIRECT_NUMBER;
+ }
+
+ // indirect block
+ let indirect_number = INODE_PER_BLOCK;
+ if block_index_within_inode < indirect_number {
+ return if let Some(indirect_block) =
+ self.get_block::<IndirectBlock>(inode.single_indirect as usize)
+ {
+ let block_index = indirect_block.block.entries[block_index_within_inode] as usize;
+ self.get_block::<T>(block_index)
+ } else {
+ None
+ };
+ } else {
+ block_index_within_inode -= indirect_number;
+ }
+
+ // double indirect block
+ let double_indirect_number = INODE_PER_BLOCK * INODE_PER_BLOCK;
+ if block_index_within_inode < double_indirect_number {
+ if let Some(double_indirect_block) =
+ self.get_block::<DoubleIndirectBlock>(inode.double_indirect as usize)
+ {
+ // 取出 double indirect block
+ let indirect_block_index = double_indirect_block.block.indirect
+ [block_index_within_inode / INODE_PER_BLOCK]
+ as usize;
+ // 要找的 entry 在 double indirect block 中的第几个 indirect block
+ if let Some(indirect_block) = self.get_block::<IndirectBlock>(indirect_block_index)
+ {
+ let block_index = indirect_block.block.entries
+ [block_index_within_inode % INODE_PER_BLOCK]
+ as usize;
+ // 拿到 DirectoryBlock 的 index
+ return self.get_block::<T>(block_index);
+ }
+ }
+ return None;
+ } else {
+ block_index_within_inode -= double_indirect_number;
+ }
+
+ // triple indirect block
+ if let Some(triple_indirect_block) =
+ self.get_block::<TripleIndirectBlock>(inode.triple_indirect as usize)
+ {
+ // 取出 triple indirect block
+ let double_indirect_block_index = triple_indirect_block.block.double_indirect
+ [block_index_within_inode / (INODE_PER_BLOCK * INODE_PER_BLOCK)]
+ as usize;
+ // 要找的 entry 在 triple indirect block 中的第几个 double indirect block
+ if let Some(double_indirect_block) =
+ self.get_block::<DoubleIndirectBlock>(double_indirect_block_index)
+ {
+ // 取出 double indirect block
+ let indirect_block_index = double_indirect_block.block.indirect
+ [block_index_within_inode % (INODE_PER_BLOCK * INODE_PER_BLOCK)
+ / INODE_PER_BLOCK] as usize;
+ // 要找的 entry 在 double indirect block 中的第几个 indirect block
+ if let Some(indirect_block) = self.get_block::<IndirectBlock>(indirect_block_index)
+ {
+ let block_index = indirect_block.block.entries
+ [block_index_within_inode % INODE_PER_BLOCK]
+ as usize;
+ // DirectoryBlock 的 index
+ return self.get_block::<T>(block_index);
+ }
+ }
+ }
+
+ None
+ }
+
+ pub(crate) fn add_direntry(
+ &mut self,
+ parent_inode: &mut Inode,
+ child_inode_index: u32,
+ child_inode_name: &OsStr,
+ child_inode: &Inode,
+ ) -> Result<u32, c_int> {
+ let entry_index = parent_inode.size;
+ let block_index_within_inode = (entry_index / 15) as usize;
+ let entry_index_within_block = (entry_index % 15) as usize;
+
+ // TODO 如果 block 内 offset 是 0 (或者访问到 block_index_within_inode 无效?)则为 Inode 分配新的 block
+
+ // TODO 把新的 entry 写到对应位置
+
+ Err(ENOSPC)
+ }
+ pub(crate) fn get_direntry(
+ &mut self,
+ inode: &Inode,
+ entry_index: u32
+ ) -> Option<DirectoryEntry> {
+ let block_index_within_inode = (entry_index / 15) as usize;
+ let entry_index_within_block = (entry_index % 15) as usize;
+
+ self.access_block::<DirectoryBlock>(inode, block_index_within_inode)
+ .map(|directory_block| {
+ directory_block.block.entries[entry_index_within_block].clone()
+ })
+ }
+
+ // pub(crate) fn get_direntry(
+ // &mut self,
+ // inode: &Inode,
+ // entry_index: u32,
+ // ) -> Option<DirectoryEntry> {
+ // // 每个 DirectoryBlock 里有 15 个 entry, 先确定 block 再确定 offset
+ // let mut block_index_within_inode = (entry_index / 15) as usize;
+ // let entry_index_within_block = (entry_index % 15) as usize;
+ //
+ // // direct block
+ // if block_index_within_inode < DIRECT_NUMBER {
+ // let block_index = inode.direct[block_index_within_inode] as usize;
+ // return self
+ // .get_block::<DirectoryBlock>(block_index)
+ // .map(|inode_block| inode_block.block.entries[entry_index_within_block].clone());
+ // } else {
+ // block_index_within_inode -= DIRECT_NUMBER;
+ // }
+ //
+ // // indirect block
+ // let indirect_number = INODE_PER_BLOCK;
+ // if block_index_within_inode < indirect_number {
+ // return if let Some(indirect_block) =
+ // self.get_block::<IndirectBlock>(inode.single_indirect as usize)
+ // {
+ // let block_index = indirect_block.block.entries[block_index_within_inode] as usize;
+ // self.get_block::<DirectoryBlock>(block_index)
+ // .map(|inode_block| inode_block.block.entries[entry_index_within_block].clone())
+ // } else {
+ // None
+ // };
+ // } else {
+ // block_index_within_inode -= indirect_number;
+ // }
+ //
+ // // double indirect block
+ // let double_indirect_number = INODE_PER_BLOCK * INODE_PER_BLOCK;
+ // if block_index_within_inode < double_indirect_number {
+ // if let Some(double_indirect_block) =
+ // self.get_block::<DoubleIndirectBlock>(inode.double_indirect as usize)
+ // {
+ // // 取出 double indirect block
+ // let indirect_block_index = double_indirect_block.block.indirect
+ // [block_index_within_inode / INODE_PER_BLOCK]
+ // as usize;
+ // // 要找的 entry 在 double indirect block 中的第几个 indirect block
+ // if let Some(indirect_block) = self.get_block::<IndirectBlock>(indirect_block_index)
+ // {
+ // let block_index = indirect_block.block.entries
+ // [block_index_within_inode % INODE_PER_BLOCK]
+ // as usize;
+ // // 拿到 DirectoryBlock 的 index
+ // return self
+ // .get_block::<DirectoryBlock>(block_index)
+ // .map(|inode_block| {
+ // inode_block.block.entries[entry_index_within_block].clone()
+ // });
+ // }
+ // }
+ // return None;
+ // } else {
+ // block_index_within_inode -= double_indirect_number;
+ // }
+ //
+ // // triple indirect block
+ // if let Some(triple_indirect_block) =
+ // self.get_block::<TripleIndirectBlock>(inode.triple_indirect as usize)
+ // {
+ // // 取出 triple indirect block
+ // let double_indirect_block_index = triple_indirect_block.block.double_indirect
+ // [block_index_within_inode / (INODE_PER_BLOCK * INODE_PER_BLOCK)]
+ // as usize;
+ // // 要找的 entry 在 triple indirect block 中的第几个 double indirect block
+ // if let Some(double_indirect_block) =
+ // self.get_block::<DoubleIndirectBlock>(double_indirect_block_index)
+ // {
+ // // 取出 double indirect block
+ // let indirect_block_index = double_indirect_block.block.indirect
+ // [block_index_within_inode % (INODE_PER_BLOCK * INODE_PER_BLOCK)
+ // / INODE_PER_BLOCK] as usize;
+ // // 要找的 entry 在 double indirect block 中的第几个 indirect block
+ // if let Some(indirect_block) = self.get_block::<IndirectBlock>(indirect_block_index)
+ // {
+ // let block_index = indirect_block.block.entries
+ // [block_index_within_inode % INODE_PER_BLOCK]
+ // as usize;
+ // // DirectoryBlock 的 index
+ // return self
+ // .get_block::<DirectoryBlock>(block_index)
+ // .map(|inode_block| {
+ // inode_block.block.entries[entry_index_within_block].clone()
+ // });
+ // }
+ // }
+ // }
+ // None
+ // }
+
+ // TODO 实现一个带 cache 的版本
+ pub fn lookup_name(&mut self, parent_inode: &Inode, name: &OsStr) -> Result<(u32, Inode), c_int> {
+ let mut entry_index = 0;
+ while entry_index < parent_inode.size {
+ if let Some(entry) = self.get_direntry(parent_inode, entry_index) {
+ if entry.name() == name {
+ let inode = self.get_inode(entry.inode as usize).unwrap().clone();
+ return Ok((entry.inode, inode))
+ }
+ }
+ entry_index += 1;
+ }
+ Err(ENOENT)
+ }
+}
diff --git a/src/memory/mod.rs b/src/memory/mod.rs
index 3380d0f..dce7c42 100644
--- a/src/memory/mod.rs
+++ b/src/memory/mod.rs
@@ -2,3 +2,4 @@
/// This is where the crucial block and inode methods presented to upper calls implemented.
pub mod cached_block;
pub mod cached_inode;
+mod inode_iter;
diff --git a/src/utils/constants.rs b/src/utils/constants.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/utils/constants.rs
@@ -0,0 +1 @@
+
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 60b09f2..0a9b825 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1,7 +1,10 @@
+mod constants;
+pub mod permissions;
+
use crate::block_device::BLOCK_SIZE;
use crate::disk::inode::{Inode, INODE_SIZE};
use crate::{AyaFS, INODE_PER_BLOCK};
-use fuser::FileAttr;
+use fuser::{FileAttr, FileType};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub(crate) fn time_now() -> u32 {
@@ -42,6 +45,62 @@ pub(crate) fn to_systime(time: u32) -> SystemTime {
UNIX_EPOCH + Duration::from_secs(time as u64)
}
+// File type code, one of:
+// 0x0 Unknown.
+// 0x1 Regular file.
+// 0x2 Directory.
+// 0x3 Character device file.
+// 0x4 Block device file.
+// 0x5 FIFO.
+// 0x6 Socket.
+// 0x7 Symbolic link.
+
+pub(crate) fn from_filetype(file_type: FileType) -> u8 {
+ match file_type {
+ FileType::NamedPipe => 0x5,
+ FileType::CharDevice => 0x3,
+ FileType::BlockDevice => 0x4,
+ FileType::Directory => 0x2,
+ FileType::RegularFile => 0x1,
+ FileType::Symlink => 0x7,
+ FileType::Socket => 0x6,
+ }
+}
+
+pub(crate) fn to_filetype(file_type: u8) -> Option<FileType> {
+ match file_type {
+ 0x0 => None,
+ 0x1 => Some(FileType::RegularFile),
+ 0x2 => Some(FileType::Directory),
+ 0x3 => Some(FileType::CharDevice),
+ 0x4 => Some(FileType::BlockDevice),
+ 0x5 => Some(FileType::NamedPipe),
+ 0x6 => Some(FileType::Socket),
+ 0x7 => Some(FileType::Symlink),
+ _ => panic!("bad filetype"),
+ }
+}
+
+pub(crate) fn to_fileattr(inode_index: usize, inode: &Inode) -> FileAttr {
+ FileAttr {
+ ino: inode_index as u64,
+ size: inode.size as u64,
+ blocks: inode.n_blocks as u64,
+ atime: to_systime(inode.atime),
+ mtime: to_systime(inode.mtime),
+ ctime: to_systime(inode.ctime),
+ crtime: 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,
+ }
+}
+
impl AyaFS {
/// 输入 inode 编号, 返回它对应的 block number 和 block 内 offset
pub(crate) fn locate_inode(&self, inode_index: usize) -> (usize, usize) {
diff --git a/src/utils/permissions.rs b/src/utils/permissions.rs
new file mode 100644
index 0000000..a3afe77
--- /dev/null
+++ b/src/utils/permissions.rs
@@ -0,0 +1,62 @@
+use std::fs::File;
+use std::io::BufRead;
+use libc::{F_OK, S_ISGID, S_ISUID, S_IXGRP, X_OK};
+use crate::disk::inode::InodeMode;
+
+pub(crate) fn get_groups(pid: u32) -> Vec<u32> {
+ let file = File::open(format!("/proc/{pid}/task/{pid}/status"))
+ .expect(format!("pid {pid} incorrect!").as_str());
+ for line in std::io::BufReader::new(file).lines() {
+ let line = line.unwrap();
+ if line.starts_with("Groups:") {
+ return line["Groups: ".len()..]
+ .split(' ')
+ .filter(|x| !x.trim().is_empty())
+ .map(|x| x.parse::<u32>().unwrap())
+ .collect();
+ }
+ }
+
+ Vec::new()
+}
+
+pub(crate) fn clear_suid_sgid(mut mode: InodeMode) -> InodeMode {
+ mode.0 &= !S_ISUID as u16;
+ if mode.0 & S_IXGRP as u16 != 0 {
+ mode.0 &= !S_ISGID as u16;
+ }
+ mode
+}
+
+pub(crate) fn check_access(
+ incoming_uid: u32,
+ incoming_gid: u32,
+ uid: u32,
+ gid: u32,
+ perm: InodeMode,
+ mut mask: i32,
+) -> bool {
+ if mask == F_OK {
+ return true;
+ }
+
+ let perm = i32::from(perm.0);
+ // root
+ if incoming_uid == 0 {
+ // 读写任何东西都是可以的, 执行只有 IXOTH/IXGRP/IXUSR 之一设置才可以
+ mask &= X_OK;
+ mask -= mask & (perm >> 6);
+ mask -= mask & (perm >> 3);
+ mask -= mask & perm;
+ return mask == 0;
+ }
+
+ if incoming_uid == uid {
+ mask -= mask & (perm >> 6);
+ } else if incoming_gid == gid {
+ mask -= mask & (perm >> 3);
+ } else {
+ mask -= mask & perm;
+ }
+ mask == 0
+} \ No newline at end of file