Support the close-on-exec file descriptor flag

This commit is contained in:
LI Qing 2023-12-13 11:29:03 +08:00 committed by Tate, Hongliang Tian
parent a6bb7c7bf5
commit 3de5c42afd
11 changed files with 153 additions and 48 deletions

View File

@ -10,6 +10,7 @@ use crate::{
fs::{ fs::{
device::{Device, DeviceId, DeviceType}, device::{Device, DeviceId, DeviceType},
devpts::DevPts, devpts::DevPts,
file_table::FdFlags,
fs_resolver::FsPath, fs_resolver::FsPath,
inode_handle::FileIo, inode_handle::FileIo,
utils::{AccessMode, Inode, InodeMode, IoctlCmd}, utils::{AccessMode, Inode, InodeMode, IoctlCmd},
@ -189,7 +190,8 @@ impl FileIo for PtyMaster {
let fd = { let fd = {
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
file_table.insert(slave) // TODO: deal with the O_CLOEXEC flag
file_table.insert(slave, FdFlags::empty())
}; };
Ok(fd) Ok(fd)
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::cell::Cell; use core::sync::atomic::{AtomicU8, Ordering};
use aster_util::slot_vec::SlotVec; use aster_util::slot_vec::SlotVec;
@ -49,20 +49,26 @@ impl FileTable {
let mode = InodeMode::S_IWUSR; let mode = InodeMode::S_IWUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap() fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
}; };
table.put(FileTableEntry::new(Arc::new(stdin), false)); table.put(FileTableEntry::new(Arc::new(stdin), FdFlags::empty()));
table.put(FileTableEntry::new(Arc::new(stdout), false)); table.put(FileTableEntry::new(Arc::new(stdout), FdFlags::empty()));
table.put(FileTableEntry::new(Arc::new(stderr), false)); table.put(FileTableEntry::new(Arc::new(stderr), FdFlags::empty()));
Self { Self {
table, table,
subject: Subject::new(), subject: Subject::new(),
} }
} }
pub fn dup(&mut self, fd: FileDescripter, new_fd: FileDescripter) -> Result<FileDescripter> { pub fn dup(
let entry = self.table.get(fd as usize).map_or_else( &mut self,
|| return_errno_with_message!(Errno::ENOENT, "No such file"), fd: FileDescripter,
|e| Ok(e.clone()), new_fd: FileDescripter,
)?; flags: FdFlags,
) -> Result<FileDescripter> {
let file = self
.table
.get(fd as usize)
.map(|entry| entry.file.clone())
.ok_or(Error::with_message(Errno::ENOENT, "No such file"))?;
// Get the lowest-numbered available fd equal to or greater than `new_fd`. // Get the lowest-numbered available fd equal to or greater than `new_fd`.
let get_min_free_fd = || -> usize { let get_min_free_fd = || -> usize {
@ -80,12 +86,13 @@ impl FileTable {
}; };
let min_free_fd = get_min_free_fd(); let min_free_fd = get_min_free_fd();
let entry = FileTableEntry::new(file, flags);
self.table.put_at(min_free_fd, entry); self.table.put_at(min_free_fd, entry);
Ok(min_free_fd as FileDescripter) Ok(min_free_fd as FileDescripter)
} }
pub fn insert(&mut self, item: Arc<dyn FileLike>) -> FileDescripter { pub fn insert(&mut self, item: Arc<dyn FileLike>, flags: FdFlags) -> FileDescripter {
let entry = FileTableEntry::new(item, false); let entry = FileTableEntry::new(item, flags);
self.table.put(entry) as FileDescripter self.table.put(entry) as FileDescripter
} }
@ -93,8 +100,9 @@ impl FileTable {
&mut self, &mut self,
fd: FileDescripter, fd: FileDescripter,
item: Arc<dyn FileLike>, item: Arc<dyn FileLike>,
flags: FdFlags,
) -> Option<Arc<dyn FileLike>> { ) -> Option<Arc<dyn FileLike>> {
let entry = FileTableEntry::new(item, false); let entry = FileTableEntry::new(item, FdFlags::empty());
let entry = self.table.put_at(fd as usize, entry); let entry = self.table.put_at(fd as usize, entry);
if entry.is_some() { if entry.is_some() {
let events = FdEvents::Close(fd); let events = FdEvents::Close(fd);
@ -131,6 +139,29 @@ impl FileTable {
closed_files closed_files
} }
pub fn close_files_on_exec(&mut self) -> Vec<Arc<dyn FileLike>> {
let mut closed_files = Vec::new();
let closed_fds: Vec<FileDescripter> = self
.table
.idxes_and_items()
.filter_map(|(idx, entry)| {
if entry.flags().contains(FdFlags::CLOEXEC) {
Some(idx as FileDescripter)
} else {
None
}
})
.collect();
for fd in closed_fds {
let entry = self.table.remove(fd as usize).unwrap();
let events = FdEvents::Close(fd);
self.notify_fd_events(&events);
entry.notify_fd_events(&events);
closed_files.push(entry.file);
}
closed_files
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn FileLike>> { pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn FileLike>> {
self.table self.table
.get(fd as usize) .get(fd as usize)
@ -196,15 +227,15 @@ impl Events for FdEvents {}
pub struct FileTableEntry { pub struct FileTableEntry {
file: Arc<dyn FileLike>, file: Arc<dyn FileLike>,
close_on_exec: Cell<bool>, flags: AtomicU8,
subject: Subject<FdEvents>, subject: Subject<FdEvents>,
} }
impl FileTableEntry { impl FileTableEntry {
pub fn new(file: Arc<dyn FileLike>, close_on_exec: bool) -> Self { pub fn new(file: Arc<dyn FileLike>, flags: FdFlags) -> Self {
Self { Self {
file, file,
close_on_exec: Cell::new(close_on_exec), flags: AtomicU8::new(flags.bits()),
subject: Subject::new(), subject: Subject::new(),
} }
} }
@ -213,6 +244,14 @@ impl FileTableEntry {
&self.file &self.file
} }
pub fn flags(&self) -> FdFlags {
FdFlags::from_bits(self.flags.load(Ordering::Relaxed)).unwrap()
}
pub fn set_flags(&self, flags: FdFlags) {
self.flags.store(flags.bits(), Ordering::Relaxed);
}
pub fn register_observer(&self, epoll: Weak<dyn Observer<FdEvents>>) { pub fn register_observer(&self, epoll: Weak<dyn Observer<FdEvents>>) {
self.subject.register_observer(epoll, ()); self.subject.register_observer(epoll, ());
} }
@ -230,8 +269,15 @@ impl Clone for FileTableEntry {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
file: self.file.clone(), file: self.file.clone(),
close_on_exec: self.close_on_exec.clone(), flags: AtomicU8::new(self.flags.load(Ordering::Relaxed)),
subject: Subject::new(), subject: Subject::new(),
} }
} }
} }
bitflags! {
pub struct FdFlags: u8 {
/// Close on exec
const CLOEXEC = 1;
}
}

View File

@ -2,7 +2,7 @@
use super::{SyscallReturn, SYS_ACCEPT}; use super::{SyscallReturn, SYS_ACCEPT};
use crate::{ use crate::{
fs::file_table::FileDescripter, fs::file_table::{FdFlags, FileDescripter},
log_syscall_entry, log_syscall_entry,
prelude::*, prelude::*,
util::net::{get_socket_from_fd, write_socket_addr_to_user}, util::net::{get_socket_from_fd, write_socket_addr_to_user},
@ -24,7 +24,7 @@ pub fn sys_accept(
let connected_fd = { let connected_fd = {
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
file_table.insert(connected_socket) file_table.insert(connected_socket, FdFlags::empty())
}; };
Ok(SyscallReturn::Return(connected_fd as _)) Ok(SyscallReturn::Return(connected_fd as _))
} }

View File

@ -1,7 +1,11 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use super::{SyscallReturn, SYS_DUP, SYS_DUP2}; use super::{SyscallReturn, SYS_DUP, SYS_DUP2};
use crate::{fs::file_table::FileDescripter, log_syscall_entry, prelude::*}; use crate::{
fs::file_table::{FdFlags, FileDescripter},
log_syscall_entry,
prelude::*,
};
pub fn sys_dup(old_fd: FileDescripter) -> Result<SyscallReturn> { pub fn sys_dup(old_fd: FileDescripter) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_DUP); log_syscall_entry!(SYS_DUP);
@ -10,7 +14,8 @@ pub fn sys_dup(old_fd: FileDescripter) -> Result<SyscallReturn> {
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let file = file_table.get_file(old_fd)?.clone(); let file = file_table.get_file(old_fd)?.clone();
let new_fd = file_table.insert(file); // The two file descriptors do not share the close-on-exec flag.
let new_fd = file_table.insert(file, FdFlags::empty());
Ok(SyscallReturn::Return(new_fd as _)) Ok(SyscallReturn::Return(new_fd as _))
} }
@ -22,7 +27,8 @@ pub fn sys_dup2(old_fd: FileDescripter, new_fd: FileDescripter) -> Result<Syscal
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let file = file_table.get_file(old_fd)?.clone(); let file = file_table.get_file(old_fd)?.clone();
if old_fd != new_fd { if old_fd != new_fd {
if let Some(old_file) = file_table.insert_at(new_fd, file) { // The two file descriptors do not share the close-on-exec flag.
if let Some(old_file) = file_table.insert_at(new_fd, file, FdFlags::empty()) {
// If the file descriptor `new_fd` was previously open, close it silently. // If the file descriptor `new_fd` was previously open, close it silently.
let _ = old_file.clean_for_close(); let _ = old_file.clean_for_close();
} }

View File

@ -7,7 +7,7 @@ use crate::{
events::IoEvents, events::IoEvents,
fs::{ fs::{
epoll::{EpollCtl, EpollEvent, EpollFile, EpollFlags}, epoll::{EpollCtl, EpollEvent, EpollFile, EpollFlags},
file_table::FileDescripter, file_table::{FdFlags, FileDescripter},
utils::CreationFlags, utils::CreationFlags,
}, },
log_syscall_entry, log_syscall_entry,
@ -26,13 +26,13 @@ pub fn sys_epoll_create1(flags: u32) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EPOLL_CREATE1); log_syscall_entry!(SYS_EPOLL_CREATE1);
debug!("flags = 0x{:x}", flags); debug!("flags = 0x{:x}", flags);
let close_on_exec = { let fd_flags = {
let flags = CreationFlags::from_bits(flags) let flags = CreationFlags::from_bits(flags)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid flags"))?; .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid flags"))?;
if flags == CreationFlags::empty() { if flags == CreationFlags::empty() {
false FdFlags::empty()
} else if flags == CreationFlags::O_CLOEXEC { } else if flags == CreationFlags::O_CLOEXEC {
true FdFlags::CLOEXEC
} else { } else {
// Only O_CLOEXEC is valid // Only O_CLOEXEC is valid
return_errno_with_message!(Errno::EINVAL, "invalid flags"); return_errno_with_message!(Errno::EINVAL, "invalid flags");
@ -42,7 +42,7 @@ pub fn sys_epoll_create1(flags: u32) -> Result<SyscallReturn> {
let current = current!(); let current = current!();
let epoll_file: Arc<EpollFile> = EpollFile::new(); let epoll_file: Arc<EpollFile> = EpollFile::new();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let fd = file_table.insert(epoll_file); let fd = file_table.insert(epoll_file, fd_flags);
Ok(SyscallReturn::Return(fd as _)) Ok(SyscallReturn::Return(fd as _))
} }

View File

@ -106,6 +106,12 @@ fn do_execve(
let current = current!(); let current = current!();
// Ensure that the file descriptors with the close-on-exec flag are closed.
let closed_files = current.file_table().lock().close_files_on_exec();
for file in closed_files {
file.clean_for_close()?;
}
debug!("load program to root vmar"); debug!("load program to root vmar");
let (new_executable_path, elf_load_info) = { let (new_executable_path, elf_load_info) = {
let fs_resolver = &*current.fs().read(); let fs_resolver = &*current.fs().read();

View File

@ -2,7 +2,10 @@
use super::{SyscallReturn, SYS_FCNTL}; use super::{SyscallReturn, SYS_FCNTL};
use crate::{ use crate::{
fs::{file_table::FileDescripter, utils::StatusFlags}, fs::{
file_table::{FdFlags, FileDescripter},
utils::StatusFlags,
},
log_syscall_entry, log_syscall_entry,
prelude::*, prelude::*,
}; };
@ -12,18 +15,37 @@ pub fn sys_fcntl(fd: FileDescripter, cmd: i32, arg: u64) -> Result<SyscallReturn
let fcntl_cmd = FcntlCmd::try_from(cmd)?; let fcntl_cmd = FcntlCmd::try_from(cmd)?;
debug!("fd = {}, cmd = {:?}, arg = {}", fd, fcntl_cmd, arg); debug!("fd = {}, cmd = {:?}, arg = {}", fd, fcntl_cmd, arg);
match fcntl_cmd { match fcntl_cmd {
FcntlCmd::F_DUPFD_CLOEXEC => { FcntlCmd::F_DUPFD => {
// FIXME: deal with the cloexec flag
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let new_fd = file_table.dup(fd, arg as FileDescripter)?; let new_fd = file_table.dup(fd, arg as FileDescripter, FdFlags::empty())?;
Ok(SyscallReturn::Return(new_fd as _)) Ok(SyscallReturn::Return(new_fd as _))
} }
FcntlCmd::F_DUPFD_CLOEXEC => {
let current = current!();
let mut file_table = current.file_table().lock();
let new_fd = file_table.dup(fd, arg as FileDescripter, FdFlags::CLOEXEC)?;
Ok(SyscallReturn::Return(new_fd as _))
}
FcntlCmd::F_GETFD => {
let current = current!();
let file_table = current.file_table().lock();
let entry = file_table.get_entry(fd)?;
let fd_flags = entry.flags();
Ok(SyscallReturn::Return(fd_flags.bits() as _))
}
FcntlCmd::F_SETFD => { FcntlCmd::F_SETFD => {
if arg != 1 { let flags = {
panic!("Unknown setfd argument"); if arg > u8::MAX.into() {
} return_errno_with_message!(Errno::EINVAL, "invalid fd flags");
// TODO: Set cloexec }
FdFlags::from_bits(arg as u8)
.ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?
};
let current = current!();
let file_table = current.file_table().lock();
let entry = file_table.get_entry(fd)?;
entry.set_flags(flags);
Ok(SyscallReturn::Return(0)) Ok(SyscallReturn::Return(0))
} }
FcntlCmd::F_GETFL => { FcntlCmd::F_GETFL => {
@ -60,7 +82,6 @@ pub fn sys_fcntl(fd: FileDescripter, cmd: i32, arg: u64) -> Result<SyscallReturn
file.set_status_flags(new_status_flags)?; file.set_status_flags(new_status_flags)?;
Ok(SyscallReturn::Return(0)) Ok(SyscallReturn::Return(0))
} }
_ => todo!(),
} }
} }

View File

@ -4,8 +4,9 @@ use super::{SyscallReturn, SYS_OPENAT};
use crate::{ use crate::{
fs::{ fs::{
file_handle::FileLike, file_handle::FileLike,
file_table::FileDescripter, file_table::{FdFlags, FileDescripter},
fs_resolver::{FsPath, AT_FDCWD}, fs_resolver::{FsPath, AT_FDCWD},
utils::CreationFlags,
}, },
log_syscall_entry, log_syscall_entry,
prelude::*, prelude::*,
@ -35,7 +36,15 @@ pub fn sys_openat(
Arc::new(inode_handle) Arc::new(inode_handle)
}; };
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let fd = file_table.insert(file_handle); let fd = {
let fd_flags =
if CreationFlags::from_bits_truncate(flags).contains(CreationFlags::O_CLOEXEC) {
FdFlags::CLOEXEC
} else {
FdFlags::empty()
};
file_table.insert(file_handle, fd_flags)
};
Ok(SyscallReturn::Return(fd as _)) Ok(SyscallReturn::Return(fd as _))
} }

View File

@ -3,9 +3,9 @@
use super::{SyscallReturn, SYS_PIPE2}; use super::{SyscallReturn, SYS_PIPE2};
use crate::{ use crate::{
fs::{ fs::{
file_table::FileDescripter, file_table::{FdFlags, FileDescripter},
pipe::{PipeReader, PipeWriter}, pipe::{PipeReader, PipeWriter},
utils::{Channel, StatusFlags}, utils::{Channel, CreationFlags, StatusFlags},
}, },
log_syscall_entry, log_syscall_entry,
prelude::*, prelude::*,
@ -27,11 +27,16 @@ pub fn sys_pipe2(fds: Vaddr, flags: u32) -> Result<SyscallReturn> {
}; };
let pipe_reader = Arc::new(reader); let pipe_reader = Arc::new(reader);
let pipe_writer = Arc::new(writer); let pipe_writer = Arc::new(writer);
let fd_flags = if CreationFlags::from_bits_truncate(flags).contains(CreationFlags::O_CLOEXEC) {
FdFlags::CLOEXEC
} else {
FdFlags::empty()
};
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
pipe_fds.reader_fd = file_table.insert(pipe_reader); pipe_fds.reader_fd = file_table.insert(pipe_reader, fd_flags);
pipe_fds.writer_fd = file_table.insert(pipe_writer); pipe_fds.writer_fd = file_table.insert(pipe_writer, fd_flags);
debug!("pipe_fds: {:?}", pipe_fds); debug!("pipe_fds: {:?}", pipe_fds);
write_val_to_user(fds, &pipe_fds)?; write_val_to_user(fds, &pipe_fds)?;

View File

@ -2,7 +2,7 @@
use super::{SyscallReturn, SYS_SOCKET}; use super::{SyscallReturn, SYS_SOCKET};
use crate::{ use crate::{
fs::file_handle::FileLike, fs::{file_handle::FileLike, file_table::FdFlags},
log_syscall_entry, log_syscall_entry,
net::socket::{ net::socket::{
ip::{DatagramSocket, StreamSocket}, ip::{DatagramSocket, StreamSocket},
@ -42,7 +42,12 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32) -> Result<SyscallRetur
let fd = { let fd = {
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
file_table.insert(file_like) let fd_flags = if sock_flags.contains(SockFlags::SOCK_CLOEXEC) {
FdFlags::CLOEXEC
} else {
FdFlags::empty()
};
file_table.insert(file_like, fd_flags)
}; };
Ok(SyscallReturn::Return(fd as _)) Ok(SyscallReturn::Return(fd as _))
} }

View File

@ -2,7 +2,7 @@
use super::{SyscallReturn, SYS_SOCKETPAIR}; use super::{SyscallReturn, SYS_SOCKETPAIR};
use crate::{ use crate::{
fs::file_table::FileDescripter, fs::file_table::{FdFlags, FileDescripter},
log_syscall_entry, log_syscall_entry,
net::socket::unix::UnixStreamSocket, net::socket::unix::UnixStreamSocket,
prelude::*, prelude::*,
@ -37,9 +37,14 @@ pub fn sys_socketpair(domain: i32, type_: i32, protocol: i32, sv: Vaddr) -> Resu
let socket_fds = { let socket_fds = {
let current = current!(); let current = current!();
let mut filetable = current.file_table().lock(); let mut file_table = current.file_table().lock();
let fd_a = filetable.insert(socket_a); let fd_flags = if sock_flags.contains(SockFlags::SOCK_CLOEXEC) {
let fd_b = filetable.insert(socket_b); FdFlags::CLOEXEC
} else {
FdFlags::empty()
};
let fd_a = file_table.insert(socket_a, fd_flags);
let fd_b = file_table.insert(socket_b, fd_flags);
SocketFds(fd_a, fd_b) SocketFds(fd_a, fd_b)
}; };