Add support for symlink and readlink syscall

This commit is contained in:
LI Qing 2023-02-20 18:20:18 +08:00 committed by Tate, Hongliang Tian
parent 4e3dfe93da
commit df82142c9d
4 changed files with 113 additions and 41 deletions

View File

@ -64,6 +64,9 @@ impl Dentry {
}
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
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);

View File

@ -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),
_ => {

View File

@ -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<SyscallReturn> {
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<usize> {
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<SyscallReturn> {
self::sys_readlinkat(AT_FDCWD, pathname_addr, usr_buf_addr, usr_buf_len)
}

View File

@ -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<SyscallReturn> {
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<SyscallReturn> {
self::sys_symlinkat(target_addr, AT_FDCWD, linkpath_addr)
}