diff --git a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs index a341b3543..6efd57469 100644 --- a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs +++ b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs @@ -64,6 +64,9 @@ impl Dentry { } pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { + if self.vnode.inode().metadata().type_ != InodeType::Dir { + return_errno!(Errno::ENOTDIR); + } let mut inner = self.inner.write(); if inner.children.get(name).is_some() { return_errno!(Errno::EEXIST); diff --git a/src/services/libs/jinux-std/src/syscall/mod.rs b/src/services/libs/jinux-std/src/syscall/mod.rs index 8e7bf0287..c3af76002 100644 --- a/src/services/libs/jinux-std/src/syscall/mod.rs +++ b/src/services/libs/jinux-std/src/syscall/mod.rs @@ -36,7 +36,7 @@ use crate::syscall::poll::sys_poll; use crate::syscall::prctl::sys_prctl; use crate::syscall::prlimit64::sys_prlimit64; use crate::syscall::read::sys_read; -use crate::syscall::readlink::sys_readlink; +use crate::syscall::readlink::{sys_readlink, sys_readlinkat}; use crate::syscall::rmdir::sys_rmdir; use crate::syscall::rt_sigaction::sys_rt_sigaction; use crate::syscall::rt_sigprocmask::sys_rt_sigprocmask; @@ -46,6 +46,7 @@ use crate::syscall::set_robust_list::sys_set_robust_list; use crate::syscall::set_tid_address::sys_set_tid_address; use crate::syscall::setpgid::sys_setpgid; use crate::syscall::stat::{sys_fstat, sys_fstatat, sys_lstat, sys_stat}; +use crate::syscall::symlink::{sys_symlink, sys_symlinkat}; use crate::syscall::tgkill::sys_tgkill; use crate::syscall::uname::sys_uname; use crate::syscall::unlink::{sys_unlink, sys_unlinkat}; @@ -101,6 +102,7 @@ mod set_robust_list; mod set_tid_address; mod setpgid; mod stat; +mod symlink; mod tgkill; mod uname; mod unlink; @@ -175,6 +177,7 @@ define_syscall_nums!( SYS_RMDIR = 84, SYS_LINK = 86, SYS_UNLINK = 87, + SYS_SYMLINK = 88, SYS_READLINK = 89, SYS_GETUID = 102, SYS_GETGID = 104, @@ -197,6 +200,8 @@ define_syscall_nums!( SYS_FSTATAT = 262, SYS_UNLINKAT = 263, SYS_LINKAT = 265, + SYS_SYMLINKAT = 266, + SYS_READLINKAT = 267, SYS_SET_ROBUST_LIST = 273, SYS_PRLIMIT64 = 302 ); @@ -292,6 +297,7 @@ pub fn syscall_dispatch( SYS_RMDIR => syscall_handler!(1, sys_rmdir, args), SYS_LINK => syscall_handler!(2, sys_link, args), SYS_UNLINK => syscall_handler!(1, sys_unlink, args), + SYS_SYMLINK => syscall_handler!(2, sys_symlink, args), SYS_READLINK => syscall_handler!(3, sys_readlink, args), SYS_GETUID => syscall_handler!(0, sys_getuid), SYS_GETGID => syscall_handler!(0, sys_getgid), @@ -314,6 +320,8 @@ pub fn syscall_dispatch( SYS_FSTATAT => syscall_handler!(4, sys_fstatat, args), SYS_UNLINKAT => syscall_handler!(3, sys_unlinkat, args), SYS_LINKAT => syscall_handler!(5, sys_linkat, args), + SYS_SYMLINKAT => syscall_handler!(3, sys_symlinkat, args), + SYS_READLINKAT => syscall_handler!(4, sys_readlinkat, args), SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args), SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args), _ => { diff --git a/src/services/libs/jinux-std/src/syscall/readlink.rs b/src/services/libs/jinux-std/src/syscall/readlink.rs index 2e92ebede..8eafc38ac 100644 --- a/src/services/libs/jinux-std/src/syscall/readlink.rs +++ b/src/services/libs/jinux-std/src/syscall/readlink.rs @@ -1,54 +1,60 @@ -use crate::{log_syscall_entry, prelude::*}; - -use crate::syscall::SYS_READLINK; -use crate::util::{read_bytes_from_user, write_bytes_to_user}; +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::{read_cstring_from_user, write_bytes_to_user}; use super::SyscallReturn; +use super::SYS_READLINKAT; -const MAX_FILENAME_LEN: usize = 128; - -pub fn sys_readlink( - filename_ptr: u64, - user_buf_ptr: u64, - user_buf_len: u64, +pub fn sys_readlinkat( + dirfd: FileDescripter, + pathname_addr: Vaddr, + usr_buf_addr: Vaddr, + usr_buf_len: usize, ) -> Result { - log_syscall_entry!(SYS_READLINK); - let res = do_sys_readlink( - filename_ptr as Vaddr, - user_buf_ptr as Vaddr, - user_buf_len as usize, - )?; - Ok(SyscallReturn::Return(res as _)) -} - -/// do sys readlink -/// write the content to user buffer, returns the actual write len -pub fn do_sys_readlink( - filename_ptr: Vaddr, - user_buf_ptr: Vaddr, - user_buf_len: usize, -) -> Result { + log_syscall_entry!(SYS_READLINKAT); + let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?; debug!( - "filename ptr = 0x{:x}, user_buf_ptr = 0x{:x}, user_buf_len = 0x{:x}", - filename_ptr, user_buf_ptr, user_buf_len + "dirfd = {}, pathname = {:?}, usr_buf_addr = 0x{:x}, usr_buf_len = 0x{:x}", + dirfd, pathname, usr_buf_addr, usr_buf_len ); - let mut filename_buffer = [0u8; MAX_FILENAME_LEN]; let current = current!(); - read_bytes_from_user(filename_ptr, &mut filename_buffer)?; - let filename = CStr::from_bytes_until_nul(&filename_buffer).expect("Invalid filename"); - debug!("filename = {:?}", filename); - if filename == CString::new("/proc/self/exe").unwrap().as_c_str() { + if pathname == CString::new("/proc/self/exe")? { // "proc/self/exe" is used to read the filename of current executable let process_file_name = current.filename().unwrap(); debug!("process exec filename= {:?}", process_file_name); - let bytes = process_file_name.as_bytes_with_nul(); - let bytes_len = bytes.len(); - let write_len = bytes_len.min(user_buf_len); - - write_bytes_to_user(user_buf_ptr, &bytes[..write_len])?; - return Ok(write_len); + // readlink does not append a terminating null byte to buf + let bytes = process_file_name.as_bytes(); + let write_len = bytes.len().min(usr_buf_len); + write_bytes_to_user(usr_buf_addr, &bytes[..write_len])?; + return Ok(SyscallReturn::Return(write_len as _)); } - panic!("does not support linkname other than /proc/self/exe") + // The common path + let dentry = { + let pathname = pathname.to_string_lossy(); + if pathname.is_empty() { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + let fs_path = FsPath::new(dirfd, pathname.as_ref())?; + current.fs().read().lookup_no_follow(&fs_path)? + }; + let linkpath = dentry.vnode().inode().read_link()?; + let bytes = linkpath.as_bytes(); + let write_len = bytes.len().min(usr_buf_len); + write_bytes_to_user(usr_buf_addr, &bytes[..write_len])?; + Ok(SyscallReturn::Return(write_len as _)) +} + +pub fn sys_readlink( + pathname_addr: Vaddr, + usr_buf_addr: Vaddr, + usr_buf_len: usize, +) -> Result { + self::sys_readlinkat(AT_FDCWD, pathname_addr, usr_buf_addr, usr_buf_len) } diff --git a/src/services/libs/jinux-std/src/syscall/symlink.rs b/src/services/libs/jinux-std/src/syscall/symlink.rs new file mode 100644 index 000000000..64cce3a39 --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/symlink.rs @@ -0,0 +1,55 @@ +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, + utils::{InodeMode, InodeType}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::SYS_SYMLINKAT; + +pub fn sys_symlinkat( + target_addr: Vaddr, + dirfd: FileDescripter, + linkpath_addr: Vaddr, +) -> Result { + log_syscall_entry!(SYS_SYMLINKAT); + let target = read_cstring_from_user(target_addr, MAX_FILENAME_LEN)?; + let linkpath = read_cstring_from_user(linkpath_addr, MAX_FILENAME_LEN)?; + debug!( + "target = {:?}, dirfd = {}, linkpath = {:?}", + target, dirfd, linkpath + ); + + let current = current!(); + let target = target.to_string_lossy(); + if target.is_empty() { + return_errno_with_message!(Errno::ENOENT, "target is empty"); + } + let (dir_dentry, link_name) = { + let linkpath = linkpath.to_string_lossy(); + if linkpath.is_empty() { + return_errno_with_message!(Errno::ENOENT, "linkpath is empty"); + } + if linkpath.ends_with("/") { + return_errno_with_message!(Errno::EISDIR, "linkpath is dir"); + } + let fs_path = FsPath::new(dirfd, linkpath.as_ref())?; + current.fs().read().lookup_dir_and_base_name(&fs_path)? + }; + + let new_dentry = dir_dentry.create( + &link_name, + InodeType::SymLink, + InodeMode::from_bits_truncate(0o777), + )?; + new_dentry.vnode().inode().write_link(&target)?; + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_symlink(target_addr: Vaddr, linkpath_addr: Vaddr) -> Result { + self::sys_symlinkat(target_addr, AT_FDCWD, linkpath_addr) +}