From f5c92834f79dfdf8007daa019f401c5e51a7596f Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Sat, 2 Dec 2023 17:37:57 -0800 Subject: Fix read/write permissions --- ayafs-core/src/filesystem/trait_impl.rs | 189 ++++++++++++++++++++++---------- 1 file changed, 133 insertions(+), 56 deletions(-) (limited to 'ayafs-core/src/filesystem') diff --git a/ayafs-core/src/filesystem/trait_impl.rs b/ayafs-core/src/filesystem/trait_impl.rs index 356cc59..39dba1a 100644 --- a/ayafs-core/src/filesystem/trait_impl.rs +++ b/ayafs-core/src/filesystem/trait_impl.rs @@ -5,13 +5,13 @@ 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 fuser::{FileType, Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyWrite, Request, TimeOrNow, ReplyStatfs, FileAttr, ReplyCreate}; 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 log::{debug, trace}; use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; use std::path::Path; @@ -22,12 +22,10 @@ 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(); } @@ -65,7 +63,7 @@ impl Filesystem for AyaFS { fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { if let Some(inode) = self.get_inode(ino as usize) { - debug!( + trace!( "getattr(ino: {}, incoming_uid: {}, inode_uid: {})", ino, _req.uid(), inode.uid, ); @@ -96,7 +94,7 @@ impl Filesystem for AyaFS { 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); + trace!("chmod on inode {:#x?} mode {:o}", ino, mode); if req.uid() != 0 && req.uid() != inode.uid { reply.error(EPERM); return; @@ -118,7 +116,7 @@ impl Filesystem for AyaFS { // chown if uid.is_some() || gid.is_some() { - debug!("chown on inode {:#x?} uid {:?} gid {:?}", ino, uid, gid); + trace!("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()) { @@ -164,7 +162,7 @@ impl Filesystem for AyaFS { // ftruncate if let Some(size) = size { // TODO 当大小减小的时候对应 deallocate 块 - debug!("ftruncate on inode {:#x?} size {:?}", ino, size); + trace!("ftruncate on inode {:#x?} size {:?}", ino, size); if let Some(file_handle) = fh { let mut inode = inode.clone(); let (inode_index, _read, write) = @@ -192,7 +190,7 @@ impl Filesystem for AyaFS { 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); + trace!("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); @@ -221,7 +219,7 @@ impl Filesystem for AyaFS { inode.ctime = current_time; } if let Some(mtime) = mtime { - debug!("utimensat on inode {:#x?}, mtime {:?}", ino, 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); @@ -259,7 +257,7 @@ impl Filesystem for AyaFS { // 这啥语义啊?? fn readlink(&mut self, req: &Request<'_>, ino: u64, reply: ReplyData) { - debug!("readlink(ino: {})", ino); + // debug!("readlink(ino: {})", ino); if let Some(inode) = self.get_inode(ino as usize) { if !inode.is_symlink() { reply.error(ENOENT); @@ -272,7 +270,7 @@ impl Filesystem for AyaFS { let path_length = inode.size as usize; let mut path = vec![0u8; path_length]; if path_length < 60 { - debug!( + trace!( "symlink path length is {}, reading directly from inode.direct", path_length ); @@ -282,7 +280,7 @@ impl Filesystem for AyaFS { }; path.as_mut_slice().copy_from_slice(copy_src); } else { - debug!( + trace!( "symlink path length is {}, using original read", path_length ); @@ -309,7 +307,7 @@ impl Filesystem for AyaFS { } } } - debug!( + trace!( "readlink path read is {:?}", OsStr::from_bytes(path.as_slice()) ); @@ -329,10 +327,10 @@ impl Filesystem for AyaFS { _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 - ); + // debug!( + // "mknod(parent: {}, name: {:?}, mode: {}, umask: {})", + // parent, name, mode, _umask + // ); let parent = parent as usize; if let Some(parent_inode) = self.get_inode(parent) { @@ -469,7 +467,7 @@ 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); let parent = parent as usize; if let Some(parent_inode) = self.get_inode(parent) { // 无权限删除 -> EACCES @@ -510,7 +508,7 @@ impl Filesystem for AyaFS { if inode.n_links == 0 { // n_links == 0 -> 整个 inode 都要删除掉 match self.remove_file(inode_index) { - Ok(flag) => debug!(" unlink {}", flag), + Ok(flag) => trace!(" unlink {}", flag), Err(err_code) => { reply.error(err_code); return; @@ -552,7 +550,7 @@ impl Filesystem for AyaFS { } fn rmdir(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!("rmdir(parent: {:#x?}, name: {:?})", parent, name); + // debug!("rmdir(parent: {:#x?}, name: {:?})", parent, name); let parent = parent as usize; if let Some(parent_inode) = self.get_inode(parent) { // 无权限删除 -> EACCES @@ -601,7 +599,7 @@ impl Filesystem for AyaFS { // directory 没有 hard link, 删了就是删了 match self.remove_dir(inode_index) { - Ok(flag) => debug!(" rmdir {}", flag), + Ok(flag) => trace!(" rmdir {}", flag), Err(err_code) => { reply.error(err_code); return; @@ -635,10 +633,10 @@ impl Filesystem for AyaFS { target: &Path, reply: ReplyEntry, ) { - debug!( - "symlink(parent: {}, name: {:?}, target: {:?})", - parent, link_name, target - ); + // debug!( + // "symlink(parent: {}, name: {:?}, target: {:?})", + // parent, link_name, target + // ); let parent = parent as usize; // let root_inode_index = 1usize; @@ -698,7 +696,7 @@ impl Filesystem for AyaFS { 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."); + trace!("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; @@ -706,7 +704,7 @@ impl Filesystem for AyaFS { }; copy_dst.copy_from_slice(target_path); } else { - debug!("create_symlink: target length >= 60, using original layout."); + trace!("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; @@ -785,10 +783,10 @@ impl Filesystem for AyaFS { flags: u32, reply: ReplyEmpty, ) { - debug!( - "rename(parent: {}, name: {:?}, new_parent: {}, new_name: {:?})", - parent, name, new_parent, new_name - ); + // 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; @@ -974,7 +972,7 @@ impl Filesystem for AyaFS { if let Err(err_code) = self.add_direntry( new_parent, &mut parent_inode, - ino as usize, + ino, new_name, from_filetype(filetype) ) { @@ -988,8 +986,78 @@ impl Filesystem for AyaFS { } } + fn create( + &mut self, + req: &Request<'_>, + parent: u64, + name: &OsStr, + mode: u32, + _umask: u32, + _flags: i32, + reply: ReplyCreate, + ) { + 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 了, 这里写回 + let fh = self.allocate_file_descriptor(child_inode_index, true, true); + reply.created(&TTL, &file_attr, 0, fh, 0); + } else { + reply.error(ENOSPC); + } + } else { + reply.error(ENOENT); + } + } + fn open(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { - debug!("open(ino: {:#x?}, flags: {:#x?})", ino, flags); + // 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 @@ -1035,7 +1103,7 @@ impl Filesystem for AyaFS { // - EOF > offset + size -> return size fn read( &mut self, - req: &Request<'_>, + _req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -1044,17 +1112,22 @@ impl Filesystem for AyaFS { _lock_owner: Option, // 用不到! reply: ReplyData, ) { - assert_eq!(self.file_handle_map.get(&fh).unwrap().0, ino as usize); + let (inode_num, read_permission, _) = self + .file_handle_map + .get(&fh) + .cloned() + .unwrap(); + assert_eq!(inode_num, 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) { + if !read_permission { reply.error(EACCES); return; } - debug!("reading inode {:#x} (offset {} size {})", ino, offset, size); + trace!("reading inode {:#x} (offset {} size {})", ino, offset, size); if offset as u64 >= inode.size { // offset 在 EOF 后面, 直接返回一个 0 长度的空 buffer @@ -1109,7 +1182,7 @@ impl Filesystem for AyaFS { // 写了多少就返回多少 fn write( &mut self, - req: &Request<'_>, + _req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -1119,17 +1192,22 @@ impl Filesystem for AyaFS { _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) { + let (inode_num, _, write_permission) = self + .file_handle_map + .get(&fh) + .cloned() + .unwrap(); + assert_eq!(inode_num, ino as usize); + if let Some(inode) = self.get_inode(inode_num) { if inode.is_dir() { reply.error(EISDIR); return; } - if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, W_OK) { + if !write_permission { reply.error(EACCES); return; } - debug!( + trace!( "writing inode {:#x} (offset {} size {})", ino, offset, @@ -1155,7 +1233,7 @@ impl Filesystem for AyaFS { // 当前块已分配, 直接往里写 if let Some(block) = self.access_block_mut::(&inode, current_block_index) { - debug!( + trace!( "writing {} bytes in block {} within inode", write_length_within_block, current_block_index ); @@ -1167,7 +1245,7 @@ impl Filesystem for AyaFS { 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); + trace!("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) @@ -1212,7 +1290,7 @@ impl Filesystem for AyaFS { } fn opendir(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { - debug!("opendir(ino: {:#x?}, flags: {:#x?})", ino, flags); + // 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 @@ -1273,10 +1351,10 @@ impl Filesystem for AyaFS { return; } - debug!( - "reading dir entries from inode {:#x} with offset {}", - ino, offset - ); + // debug!( + // "reading dir entries from inode {:#x} with offset {}", + // ino, offset + // ); let inode = inode.clone(); self.load_direntry_map(ino as usize, &inode).unwrap(); @@ -1288,10 +1366,10 @@ impl Filesystem for AyaFS { .enumerate() .skip(offset as usize) { - debug!( - " entry {} from inode {:#x} with name {:?}", - entry_index, dir_entry.inode, name - ); + // 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, @@ -1349,10 +1427,9 @@ impl Filesystem for AyaFS { // 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); + // 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 权限 -- cgit v1.2.3-70-g09d2