diff --git a/os/src/fs/proc/inode.rs b/os/src/fs/proc/inode.rs index 78b87d4c..d411ea8d 100644 --- a/os/src/fs/proc/inode.rs +++ b/os/src/fs/proc/inode.rs @@ -176,7 +176,7 @@ impl ProcInode { metadata: SpinLock::new(InodeMetadata { inode_no, inode_type: InodeType::Symlink, - mode: FileMode::from_bits_truncate(0o777), + mode: FileMode::from_bits_truncate(0o777 | FileMode::S_IFLNK.bits()), uid: 0, gid: 0, size: target.len(), @@ -211,7 +211,7 @@ impl ProcInode { metadata: SpinLock::new(InodeMetadata { inode_no, inode_type: InodeType::Symlink, - mode: FileMode::from_bits_truncate(0o777), + mode: FileMode::from_bits_truncate(0o777 | FileMode::S_IFLNK.bits()), uid: 0, gid: 0, size: 0, // 动态链接的大小未知 diff --git a/os/src/kernel/syscall/task/clone_ops.rs b/os/src/kernel/syscall/task/clone_ops.rs index ea98d235..5098a94f 100644 --- a/os/src/kernel/syscall/task/clone_ops.rs +++ b/os/src/kernel/syscall/task/clone_ops.rs @@ -46,6 +46,7 @@ pub fn clone( fs, uts, rlimit, + exe_path, ) = { let _guard = crate::sync::PreemptGuard::new(); let cpu = current_cpu(); @@ -66,6 +67,7 @@ pub fn clone( task.fs.clone(), task.uts_namespace.clone(), task.rlimit.clone(), + task.exe_path.clone(), ) }; let exit_signal = requested_flags.get_exit_signal(); @@ -132,6 +134,7 @@ pub fn clone( fd_table, fs, ); + child_task.exe_path = exe_path; if requested_flags.contains(CloneFlags::CHILD_SETTID) { unsafe { diff --git a/os/src/kernel/syscall/task/exec_ops.rs b/os/src/kernel/syscall/task/exec_ops.rs index 0f590d18..80a02aed 100644 --- a/os/src/kernel/syscall/task/exec_ops.rs +++ b/os/src/kernel/syscall/task/exec_ops.rs @@ -41,7 +41,7 @@ pub fn execve( Err(_) => return -EIO, }; if meta.inode_type != crate::vfs::InodeType::File { - return -EISDIR; + return exec_non_file_errno(meta.inode_type); } let prefix_len = core::cmp::min(meta.size, 256); @@ -173,6 +173,13 @@ fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), HashbangError> { Ok((interpreter_path, interpreter_arg)) } +// 按 POSIX/Linux 语义:对非普通文件(含目录)调用 execve 一律返回 EACCES。 +// 真实内核 may_open() 对 S_IFDIR + MAY_EXEC 返回 -EACCES;EISDIR 仅用于写目录等场景。 +// busybox/bash 拿到 EACCES 后会自行处理(bash 会 stat 后输出 "Is a directory")。 +fn exec_non_file_errno(_inode_type: crate::vfs::InodeType) -> c_int { + -EACCES +} + /// 执行一个新程序(execve)的准备阶段:解析 ELF 并创建新的地址空间 fn do_execve_prepare( path: &str, @@ -181,6 +188,9 @@ fn do_execve_prepare( Ok(p) => p, Err(crate::kernel::task::ExecImageError::Fs(FsError::NotFound)) => return Err(-ENOENT), Err(crate::kernel::task::ExecImageError::Fs(FsError::IsDirectory)) => return Err(-EISDIR), + Err(crate::kernel::task::ExecImageError::NotRegular(inode_type)) => { + return Err(exec_non_file_errno(inode_type)); + } Err(crate::kernel::task::ExecImageError::Fs(_)) => return Err(-EIO), Err(crate::kernel::task::ExecImageError::Paging( crate::mm::page_table::PagingError::OutOfMemory, diff --git a/os/src/kernel/syscall/task/mod.rs b/os/src/kernel/syscall/task/mod.rs index 05459fe4..2a830f13 100644 --- a/os/src/kernel/syscall/task/mod.rs +++ b/os/src/kernel/syscall/task/mod.rs @@ -30,8 +30,8 @@ use crate::{ sync::SpinLock, uapi::{ errno::{ - EAGAIN, EFAULT, EINTR, EINVAL, EIO, EISDIR, ENOENT, ENOEXEC, ENOMEM, ENOSYS, EPERM, - ESRCH, ETIMEDOUT, + EACCES, EAGAIN, EFAULT, EINTR, EINVAL, EIO, EISDIR, ENOENT, ENOEXEC, ENOMEM, ENOSYS, + EPERM, ESRCH, ETIMEDOUT, }, futex::{FUTEX_CLOCK_REALTIME, FUTEX_PRIVATE, FUTEX_WAIT, FUTEX_WAKE, RobustListHead}, resource::{RLIM_NLIMITS, Rlimit, Rusage}, diff --git a/os/src/kernel/task/exec_loader.rs b/os/src/kernel/task/exec_loader.rs index 18849639..666da83f 100644 --- a/os/src/kernel/task/exec_loader.rs +++ b/os/src/kernel/task/exec_loader.rs @@ -13,6 +13,7 @@ use crate::vfs::{FsError, Inode, InodeType}; pub enum ExecImageError { Fs(FsError), InvalidElf, + NotRegular(InodeType), Paging(PagingError), } @@ -429,7 +430,7 @@ pub fn prepare_exec_image_from_path(path: &str) -> Result Result()); + // 原先:清零整个 TrapFrame,避免旧值泄漏到用户态。 + // 注意:write_bytes 的 count 以 TrapFrame 为单位(非字节), + // 这里只清零 1 个 TrapFrame。若误传 size_of::(), + // 会写 280*280 字节、越界冲掉当前 satp 指向的页表,导致静默死循环。 + // 由于 set_exec_trap_frame 内部已 *self = Self::zero_init() 全量清零, + // 这行 write_bytes 在功能上完全冗余,故直接删除 ::set_exec_trap_frame_from_layout( &mut *tf_ptr, initial_pc.as_usize(), diff --git a/os/src/vfs/adapter.rs b/os/src/vfs/adapter.rs index 8acc143f..03c27d56 100644 --- a/os/src/vfs/adapter.rs +++ b/os/src/vfs/adapter.rs @@ -12,7 +12,7 @@ impl Stat { Self { st_dev: 0, // TODO: 需要从文件系统获取设备号 st_ino: meta.inode_no as u64, - st_mode: meta.mode.bits(), + st_mode: mode_with_type(meta), st_nlink: meta.nlinks as u32, st_uid: meta.uid, st_gid: meta.gid, @@ -50,7 +50,7 @@ impl Statx { stx_nlink: meta.nlinks as u32, stx_uid: meta.uid, stx_gid: meta.gid, - stx_mode: meta.mode.bits() as u16, + stx_mode: mode_with_type(meta) as u16, __spare0: [0; 1], stx_ino: meta.inode_no as u64, stx_size: meta.size as u64, @@ -72,6 +72,23 @@ impl Statx { } } +fn mode_with_type(meta: &InodeMetadata) -> u32 { + let mode = meta.mode.bits(); + if mode & crate::vfs::FileMode::S_IFMT.bits() != 0 { + return mode; + } + + mode | match meta.inode_type { + InodeType::File => crate::vfs::FileMode::S_IFREG.bits(), + InodeType::Directory => crate::vfs::FileMode::S_IFDIR.bits(), + InodeType::Symlink => crate::vfs::FileMode::S_IFLNK.bits(), + InodeType::CharDevice => crate::vfs::FileMode::S_IFCHR.bits(), + InodeType::BlockDevice => crate::vfs::FileMode::S_IFBLK.bits(), + InodeType::Fifo => crate::vfs::FileMode::S_IFIFO.bits(), + InodeType::Socket => crate::vfs::FileMode::S_IFSOCK.bits(), + } +} + /// 将InodeType转换为d_type值 pub fn inode_type_to_d_type(t: InodeType) -> u8 { match t {