use alloc::sync::Arc; use log::warn; use system_error::SystemError; use super::{ fcntl::AtFlags, file::{File, FileMode}, syscall::{ModeType, OpenHow, OpenHowResolve}, utils::{rsplit_path, user_path_at}, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; use crate::{ driver::base::block::SeekFrom, process::ProcessManager, syscall::user_access::check_and_clone_cstr, }; use crate::{filesystem::vfs::syscall::UtimensFlags, process::cred::Kgid}; use crate::{ process::cred::GroupInfo, time::{syscall::PosixTimeval, PosixTimeSpec}, }; use alloc::string::String; pub(super) fn do_faccessat( dirfd: i32, path: *const u8, mode: ModeType, flags: u32, ) -> Result { if (mode.bits() & (!ModeType::S_IRWXO.bits())) != 0 { return Err(SystemError::EINVAL); } if (flags & (!((AtFlags::AT_EACCESS | AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH).bits() as u32))) != 0 { return Err(SystemError::EINVAL); } // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0; let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let path = path.to_str().map_err(|_| SystemError::EINVAL)?; let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; // todo: 接着完善(可以借鉴linux 6.1.9的do_faccessat) return Ok(0); } pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result { let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let path = path.to_str().map_err(|_| SystemError::EINVAL)?; let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; warn!("do_fchmodat: not implemented yet\n"); // todo: 真正去改变文件的权限 return Ok(0); } pub fn do_fchownat( dirfd: i32, path: &str, uid: usize, gid: usize, flag: AtFlags, ) -> Result { // 检查flag是否合法 if flag.contains(!(AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH)) { return Err(SystemError::EINVAL); } let follow_symlink = flag.contains(!AtFlags::AT_SYMLINK_NOFOLLOW); let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; // 如果找不到文件,则返回错误码ENOENT let inode = if follow_symlink { inode.lookup_follow_symlink2(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES, false) } else { inode.lookup(path.as_str()) }; if inode.is_err() { let errno = inode.clone().unwrap_err(); // 文件不存在 if errno == SystemError::ENOENT { return Err(SystemError::ENOENT); } } let inode = inode.unwrap(); return chown_common(inode, uid, gid); } fn chown_common(inode: Arc, uid: usize, gid: usize) -> Result { let mut meta = inode.metadata()?; let cred = ProcessManager::current_pcb().cred(); let current_uid = cred.uid.data(); let current_gid = cred.gid.data(); let mut group_info = GroupInfo::default(); if let Some(info) = cred.group_info.as_ref() { group_info = info.clone(); } // 检查权限 match current_uid { 0 => { meta.uid = uid; meta.gid = gid; } _ => { // 非文件所有者不能更改信息,且不能更改uid if current_uid != meta.uid || uid != meta.uid { return Err(SystemError::EPERM); } if gid != current_gid && !group_info.gids.contains(&Kgid::from(gid)) { return Err(SystemError::EPERM); } meta.gid = gid; } } meta.mode.remove(ModeType::S_ISUID | ModeType::S_ISGID); inode.set_metadata(&meta)?; return Ok(0); } pub fn ksys_fchown(fd: i32, uid: usize, gid: usize) -> Result { let fd_table = &ProcessManager::current_pcb().fd_table(); let fd_table = fd_table.read(); let inode = fd_table.get_file_by_fd(fd).unwrap().inode(); let result = chown_common(inode, uid, gid); drop(fd_table); return result; } pub(super) fn do_sys_open( dfd: i32, path: &str, o_flags: FileMode, mode: ModeType, follow_symlink: bool, ) -> Result { let how = OpenHow::new(o_flags, mode, OpenHowResolve::empty()); return do_sys_openat2(dfd, path, how, follow_symlink); } fn do_sys_openat2( dirfd: i32, path: &str, how: OpenHow, follow_symlink: bool, ) -> Result { // debug!("open path: {}, how: {:?}", path, how); let path = path.trim(); let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; let inode: Result, SystemError> = inode_begin.lookup_follow_symlink( &path, if follow_symlink { VFS_MAX_FOLLOW_SYMLINK_TIMES } else { 0 }, ); let inode: Arc = match inode { Ok(inode) => inode, Err(errno) => { // 文件不存在,且需要创建 if how.o_flags.contains(FileMode::O_CREAT) && !how.o_flags.contains(FileMode::O_DIRECTORY) && errno == SystemError::ENOENT { let (filename, parent_path) = rsplit_path(&path); // 查找父目录 let parent_inode: Arc = ROOT_INODE().lookup(parent_path.unwrap_or("/"))?; // 创建文件 let inode: Arc = parent_inode.create( filename, FileType::File, ModeType::from_bits_truncate(0o755), )?; inode } else { // 不需要创建文件,因此返回错误码 return Err(errno); } } }; let file_type: FileType = inode.metadata()?.file_type; // 如果要打开的是文件夹,而目标不是文件夹 if how.o_flags.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir { return Err(SystemError::ENOTDIR); } // 创建文件对象 let file: File = File::new(inode, how.o_flags)?; // 打开模式为“追加” if how.o_flags.contains(FileMode::O_APPEND) { file.lseek(SeekFrom::SeekEnd(0))?; } // 如果O_TRUNC,并且,打开模式包含O_RDWR或O_WRONLY,清空文件 if how.o_flags.contains(FileMode::O_TRUNC) && (how.o_flags.contains(FileMode::O_RDWR) || how.o_flags.contains(FileMode::O_WRONLY)) && file_type == FileType::File { file.ftruncate(0)?; } // 把文件对象存入pcb let r = ProcessManager::current_pcb() .fd_table() .write() .alloc_fd(file, None) .map(|fd| fd as usize); return r; } /// On Linux, futimens() is a library function implemented on top of /// the utimensat() system call. To support this, the Linux /// utimensat() system call implements a nonstandard feature: if /// pathname is NULL, then the call modifies the timestamps of the /// file referred to by the file descriptor dirfd (which may refer to /// any type of file). pub fn do_utimensat( dirfd: i32, pathname: Option, times: Option<[PosixTimeSpec; 2]>, flags: UtimensFlags, ) -> Result { const UTIME_NOW: i64 = (1i64 << 30) - 1i64; const UTIME_OMIT: i64 = (1i64 << 30) - 2i64; // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags); let inode = match pathname { Some(path) => { let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?; let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) { inode_begin.lookup(path.as_str())? } else { inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)? }; inode } None => { let binding = ProcessManager::current_pcb().fd_table(); let fd_table_guard = binding.write(); let file = fd_table_guard .get_file_by_fd(dirfd) .ok_or(SystemError::EBADF)?; file.inode() } }; let now = PosixTimeSpec::now(); let mut meta = inode.metadata()?; if let Some([atime, mtime]) = times { if atime.tv_nsec == UTIME_NOW { meta.atime = now; } else if atime.tv_nsec != UTIME_OMIT { meta.atime = atime; } if mtime.tv_nsec == UTIME_NOW { meta.mtime = now; } else if mtime.tv_nsec != UTIME_OMIT { meta.mtime = mtime; } inode.set_metadata(&meta).unwrap(); } else { meta.atime = now; meta.mtime = now; inode.set_metadata(&meta).unwrap(); } return Ok(0); } pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result { // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times); let (inode_begin, path) = user_path_at( &ProcessManager::current_pcb(), AtFlags::AT_FDCWD.bits(), path, )?; let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; let mut meta = inode.metadata()?; if let Some([atime, mtime]) = times { meta.atime = PosixTimeSpec::from(atime); meta.mtime = PosixTimeSpec::from(mtime); inode.set_metadata(&meta)?; } else { let now = PosixTimeSpec::now(); meta.atime = now; meta.mtime = now; inode.set_metadata(&meta)?; } return Ok(0); }