From 4e76ed5fea684995cc06acf3c75a9b3a7be94f26 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Thu, 4 Dec 2025 23:06:55 +0800 Subject: [PATCH] Migrate to new ioctl infrastructure --- kernel/libs/aster-util/src/safe_ptr.rs | 44 ++++++- kernel/src/device/evdev/file.rs | 9 +- kernel/src/device/fb.rs | 114 ++++++++++-------- kernel/src/device/misc/tdxguest.rs | 82 +++++++------ kernel/src/device/pty/file.rs | 5 +- kernel/src/device/pty/master.rs | 67 +++++++---- kernel/src/device/tty/ioctl_defs.rs | 28 +++++ kernel/src/device/tty/mod.rs | 71 ++++++----- kernel/src/device/tty/n_tty.rs | 5 +- kernel/src/device/tty/termio.rs | 6 +- kernel/src/fs/epoll/file.rs | 7 +- kernel/src/fs/file_handle.rs | 5 +- kernel/src/fs/inode_handle/dyn_cap.rs | 7 +- kernel/src/fs/inode_handle/mod.rs | 9 +- kernel/src/fs/notify/inotify.rs | 24 ++-- kernel/src/fs/ramfs/memfd.rs | 7 +- kernel/src/fs/utils/ioctl_defs.rs | 7 ++ kernel/src/fs/utils/mod.rs | 1 + kernel/src/process/process/terminal.rs | 66 +++++++---- kernel/src/syscall/ioctl.rs | 158 ++++++++++++++++--------- 20 files changed, 450 insertions(+), 272 deletions(-) create mode 100644 kernel/src/device/tty/ioctl_defs.rs create mode 100644 kernel/src/fs/utils/ioctl_defs.rs diff --git a/kernel/libs/aster-util/src/safe_ptr.rs b/kernel/libs/aster-util/src/safe_ptr.rs index cc9a3300c..d4537ef3a 100644 --- a/kernel/libs/aster-util/src/safe_ptr.rs +++ b/kernel/libs/aster-util/src/safe_ptr.rs @@ -6,8 +6,11 @@ use aster_rights::{Dup, Exec, Full, Read, Signal, TRightSet, TRights, Write}; use aster_rights_proc::require; use inherit_methods_macro::inherit_methods; use ostd::{ - mm::{Daddr, DmaStream, HasDaddr, HasPaddr, Paddr, PodOnce, VmIo, VmIoOnce}, - Pod, Result, + mm::{ + io_util::{HasVmReaderWriter, VmReaderWriterTypes}, + Daddr, DmaStream, HasDaddr, HasPaddr, Paddr, PodOnce, VmIo, VmIoOnce, + }, + Error, Pod, Result, }; /// Safe pointers. @@ -180,7 +183,7 @@ impl HasPaddr for SafePtr { // =============== Read and write methods ============== impl SafePtr> { - /// Read the value from the pointer. + /// Reads the value from the pointer. /// /// # Access rights /// @@ -190,7 +193,7 @@ impl SafePtr> { self.vm_obj.read_val(self.offset) } - /// Read a slice of values from the pointer. + /// Reads a slice of values from the pointer. /// /// # Access rights /// @@ -200,7 +203,7 @@ impl SafePtr> { self.vm_obj.read_slice(self.offset, slice) } - /// Overwrite the value at the pointer. + /// Overwrites the value at the pointer. /// /// # Access rights /// @@ -210,7 +213,7 @@ impl SafePtr> { self.vm_obj.write_val(self.offset, val) } - /// Overwrite a slice of values at the pointer. + /// Overwrites a slice of values at the pointer. /// /// # Access rights /// @@ -219,6 +222,35 @@ impl SafePtr> { pub fn write_slice(&self, slice: &[T]) -> Result<()> { self.vm_obj.write_slice(self.offset, slice) } + + /// Copies the value from another pointer. + /// + /// # Errors + /// + /// This method will fail if + /// - either pointer's underlying VM object is too short to hold `T` or + /// - the memory copy between two pointers fails. + /// + /// # Access rights + /// + /// This method requires + /// - the Write right for this pointer and + /// - the Read right for another pointer. + #[require(R > Write)] + #[require(R1 > Read)] + pub fn copy_from( + &self, + ptr: &SafePtr>, + ) -> Result<()> { + let mut reader = M1::Types::to_reader_result(ptr.vm_obj.reader())?.to_fallible(); + + if reader.remain() < size_of::() { + return Err(Error::InvalidArgs); + } + reader.limit(size_of::()); + + self.vm_obj.write(self.offset, &mut reader) + } } // =============== Read and write methods ============== diff --git a/kernel/src/device/evdev/file.rs b/kernel/src/device/evdev/file.rs index 8d68f4db5..534680f2c 100644 --- a/kernel/src/device/evdev/file.rs +++ b/kernel/src/device/evdev/file.rs @@ -23,11 +23,14 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{InodeIo, IoctlCmd, StatusFlags}, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable, Pollee}, - util::ring_buffer::{RbConsumer, RbProducer, RingBuffer}, + util::{ + ioctl::RawIoctl, + ring_buffer::{RbConsumer, RbProducer, RingBuffer}, + }, }; pub(super) const EVDEV_BUFFER_SIZE: usize = 64; @@ -289,7 +292,7 @@ impl FileIo for EvdevFile { false } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { + fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result { // TODO: Support ioctl operations for evdev files. // Most ioctl implementations return `ENOTTY` for invalid ioctl commands, representing "The diff --git a/kernel/src/device/fb.rs b/kernel/src/device/fb.rs index f36bc08fe..c27eaa43a 100644 --- a/kernel/src/device/fb.rs +++ b/kernel/src/device/fb.rs @@ -17,10 +17,11 @@ use crate::{ device::{Device, DeviceType}, file_handle::Mappable, inode_handle::FileIo, - utils::{InodeIo, IoctlCmd, StatusFlags}, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; #[derive(Debug)] @@ -202,6 +203,23 @@ struct FbCmapUser { pub transp: usize, } +mod ioctl_defs { + use super::{FbCmapUser, FbFixScreenInfo, FbVarScreenInfo}; + use crate::util::ioctl::{ioc, InData, InOutData, NoData, OutData}; + + // Reference: + + pub(super) type GetVarScreenInfo = ioc!(FBIOGET_VSCREENINFO, 0x4600, OutData); + pub(super) type PutVarScreenInfo = ioc!(FBIOPUT_VSCREENINFO, 0x4601, InOutData); + pub(super) type GetFixScreenInfo = ioc!(FBIOGET_FSCREENINFO, 0x4602, OutData); + pub(super) type GetColorMap = ioc!(FBIOGETCMAP, 0x4604, InData); + pub(super) type PutColorMap = ioc!(FBIOPUTCMAP, 0x4605, InData); + + // `NoData` is used below because they're not supported by efifb. + pub(super) type PanDisplay = ioc!(FBIOPAN_DISPLAY, 0x4606, NoData); + pub(super) type Blank = ioc!(FBIOBLANK, 0x4611, NoData); +} + impl Device for Fb { fn type_(&self) -> DeviceType { DeviceType::Char @@ -247,12 +265,8 @@ impl FbHandle { Ok(()) } - /// Handles the [`IoctlCmd::GETVSCREENINFO`] ioctl command. - /// - /// Arguments: - /// - Input: None. - /// - Output: [`FbVarScreenInfo`]. - fn handle_get_var_screen_info(&self, arg: usize) -> Result { + /// Collects the information in the [`FbVarScreenInfo`]. + fn collect_var_screen_info(&self) -> FbVarScreenInfo { /// Default pixel clock calculation for efifb compatibility const DEFAULT_PIXEL_CLOCK_DIVISOR: u32 = 10_000_000; @@ -265,7 +279,7 @@ impl FbHandle { let pixel_format = self.framebuffer.pixel_format(); let (red, green, blue, transp) = FbBitfield::from_pixel_format(pixel_format); - let screen_info = FbVarScreenInfo { + FbVarScreenInfo { xres: self.framebuffer.width() as u32, yres: self.framebuffer.height() as u32, xres_virtual: self.framebuffer.width() as u32, @@ -284,39 +298,27 @@ impl FbHandle { vsync_len: DEFAULT_VSYNC_LEN, hsync_len: (self.framebuffer.width() as u32 / 8) & 0xf8, ..Default::default() - }; - - current_userspace!().write_val(arg, &screen_info)?; - Ok(0) + } } - /// Handles the [`IoctlCmd::GETFSCREENINFO`] ioctl command. - /// - /// Arguments: - /// - Input: None. - /// - Output: [`FbFixScreenInfo`]. - fn handle_get_fix_screen_info(&self, arg: usize) -> Result { - let screen_info = FbFixScreenInfo { + /// Collects the information in the [`FbFixScreenInfo`]. + fn collect_fix_screen_info(&self) -> FbFixScreenInfo { + FbFixScreenInfo { smem_start: self.framebuffer.io_mem().paddr() as u64, smem_len: self.framebuffer.io_mem().size() as u32, line_length: self.framebuffer.line_size() as u32, ..Default::default() - }; - - current_userspace!().write_val(arg, &screen_info)?; - Ok(0) + } } - /// Handles the [`IoctlCmd::GETCMAP`] ioctl command. + /// Handles the [`ioctl_defs::GetColorMap`] ioctl command. /// /// Arguments: /// - Input: [`FbCmapUser`] (specifying the range). /// - Output: [`FbCmapUser`] (filled with color palette data). - fn handle_get_cmap(&self, arg: usize) -> Result { - let cmap_user: FbCmapUser = current_userspace!().read_val(arg)?; - + fn handle_get_cmap(&self, cmap_user: &FbCmapUser) -> Result<()> { if cmap_user.len == 0 { - return Ok(0); + return Ok(()); } let start = cmap_user.start as usize; @@ -340,19 +342,17 @@ impl FbHandle { Self::write_color_maps_to_user(cmap_user.transp, &transp)?; } - Ok(0) + Ok(()) } - /// Handles the [`IoctlCmd::PUTCMAP`] ioctl command. + /// Handles the [`ioctl_defs::PutColorMap`] ioctl command. /// /// Arguments: /// - Input: [`FbCmapUser`] (with color palette data). /// - Output: None. - fn handle_set_cmap(&self, arg: usize) -> Result { - let cmap_user: FbCmapUser = current_userspace!().read_val(arg)?; - + fn handle_set_cmap(&self, cmap_user: &FbCmapUser) -> Result<()> { if cmap_user.len == 0 { - return Ok(0); + return Ok(()); } let start = cmap_user.start as usize; @@ -392,7 +392,7 @@ impl FbHandle { // Set color map entries in framebuffer self.framebuffer.set_color_map(start, &entries)?; - Ok(0) + Ok(()) } } @@ -475,19 +475,34 @@ impl FileIo for FbHandle { Ok(Mappable::IoMem(iomem.clone())) } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - match cmd { - IoctlCmd::GETVSCREENINFO => self.handle_get_var_screen_info(arg), - IoctlCmd::GETFSCREENINFO => self.handle_get_fix_screen_info(arg), - IoctlCmd::PUTVSCREENINFO => { + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { + use ioctl_defs::*; + + dispatch_ioctl!(match raw_ioctl { + cmd @ GetVarScreenInfo => { + cmd.write(&self.collect_var_screen_info())?; + Ok(0) + } + cmd @ PutVarScreenInfo => { // EFI framebuffers do not support changing settings. Linux // will return the old settings to user space and succeed. // Reference: . - self.handle_get_var_screen_info(arg) + cmd.write(&self.collect_var_screen_info())?; + Ok(0) } - IoctlCmd::GETCMAP => self.handle_get_cmap(arg), - IoctlCmd::PUTCMAP => self.handle_set_cmap(arg), - IoctlCmd::PANDISPLAY | IoctlCmd::FBIOBLANK => { + cmd @ GetFixScreenInfo => { + cmd.write(&self.collect_fix_screen_info())?; + Ok(0) + } + cmd @ GetColorMap => { + self.handle_get_cmap(&cmd.read()?)?; + Ok(0) + } + cmd @ PutColorMap => { + self.handle_set_cmap(&cmd.read()?)?; + Ok(0) + } + PanDisplay | Blank => { // These commands are not supported by efifb. // We return errors according to the Linux behavior. return_errno_with_message!( @@ -497,15 +512,12 @@ impl FileIo for FbHandle { } _ => { log::debug!( - "the ioctl command {:?} is not supported by framebuffer devices", - cmd + "the ioctl command {:#x} is unknown for framebuffer devices", + raw_ioctl.cmd() ); - return_errno_with_message!( - Errno::ENOTTY, - "the ioctl command is not supported by framebuffer devices" - ) + return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"); } - } + }) } } diff --git a/kernel/src/device/misc/tdxguest.rs b/kernel/src/device/misc/tdxguest.rs index 24f0ce0d9..d01f7fa8f 100644 --- a/kernel/src/device/misc/tdxguest.rs +++ b/kernel/src/device/misc/tdxguest.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -use alloc::sync::{Arc, Weak}; +use alloc::sync::Arc; use core::{ mem::{offset_of, size_of}, time::Duration, @@ -10,10 +10,7 @@ use aster_util::{field_ptr, safe_ptr::SafePtr}; use device_id::{DeviceId, MinorId}; use ostd::{ const_assert, - mm::{ - io_util::HasVmReaderWriter, DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, USegment, - VmIo, PAGE_SIZE, - }, + mm::{DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, USegment, VmIo, PAGE_SIZE}, sync::WaitQueue, }; use tdx_guest::{ @@ -27,10 +24,11 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::{InodeIo, IoctlCmd, StatusFlags}, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; const TDX_GUEST_MINOR: u32 = 0x7b; @@ -151,11 +149,26 @@ impl FileIo for TdxGuestFile { false } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - match cmd { - IoctlCmd::TDXGETREPORT => handle_get_report(arg), - _ => return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"), - } + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { + use ioctl_defs::*; + + dispatch_ioctl!(match raw_ioctl { + cmd @ GetTdxReport => { + cmd.with_data_ptr(|data_ptr| { + let inblob = { + let inblob_ptr = field_ptr!(&data_ptr, TdxReportRequest, report_data); + inblob_ptr.read()? + }; + + let outblob = tdx_get_report(inblob.as_bytes())?; + let outblob_ptr = field_ptr!(&data_ptr, TdxReportRequest, tdx_report); + outblob_ptr.copy_from(&outblob)?; + + Ok(0) + }) + } + _ => return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"), + }) } } @@ -164,17 +177,16 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result> { const GET_QUOTE_BUF_SIZE: usize = 8 * 1024; let report = tdx_get_report(inblob)?; + let buf = alloc_dma_buf(GET_QUOTE_BUF_SIZE)?; let report_ptr: SafePtr = SafePtr::new(&buf, 0); + let payload_ptr: SafePtr = SafePtr::new(&buf, size_of::()); field_ptr!(&report_ptr, TdxQuoteHdr, version).write(&1u64)?; field_ptr!(&report_ptr, TdxQuoteHdr, status).write(&0u64)?; field_ptr!(&report_ptr, TdxQuoteHdr, in_len).write(&(size_of::() as u32))?; field_ptr!(&report_ptr, TdxQuoteHdr, out_len).write(&0u32)?; - buf.write( - size_of::(), - report.reader().to_fallible().limit(size_of::()), - )?; + payload_ptr.copy_from(&report)?; // FIXME: The `get_quote` API from the `tdx_guest` crate should have been marked `unsafe` // because it has no way to determine if the input physical address is safe or not. @@ -198,7 +210,7 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result> { // "Accept a pending private page and initialize it to all-0 using the TD ephemeral private key." let out_len = field_ptr!(&report_ptr, TdxQuoteHdr, out_len).read()?; let mut outblob = vec![0u8; out_len as usize].into_boxed_slice(); - buf.read_bytes(size_of::(), outblob.as_mut())?; + payload_ptr.cast().read_slice(outblob.as_mut())?; Ok(outblob) } @@ -221,10 +233,10 @@ struct TdxReportRequest { tdx_report: TdReport, } -impl TdxReportRequest { - fn report_inblob(&self) -> &[u8] { - self.report_data.as_bytes() - } +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct ReportData { + data: [u8; 64], } /// TDX Report structure (`TDREPORT_STRUCT`) as defined in the Intel TDX Module Specification. @@ -262,38 +274,24 @@ struct TdInfo { extension: [u8; 64], } -#[derive(Debug, Clone, Copy, Pod)] -#[repr(C)] -struct ReportData { - data: [u8; 64], -} - impl TdReport { const fn report_data_offset() -> usize { offset_of!(TdReport, report_mac) + offset_of!(ReportMac, report_data) } } -fn handle_get_report(arg: usize) -> Result { - let current_task = ostd::task::Task::current().unwrap(); - let user_space = CurrentUserSpace::new(current_task.as_thread_local().unwrap()); - let user_request: TdxReportRequest = user_space.read_val(arg)?; +mod ioctl_defs { + use super::TdxReportRequest; + use crate::util::ioctl::{ioc, InOutData}; - let report = tdx_get_report(&user_request.report_inblob())?; + // Reference: - let tdx_report_vaddr = arg + offset_of!(TdxReportRequest, tdx_report); - user_space.write_bytes( - tdx_report_vaddr, - report.reader().limit(size_of::()), - )?; - Ok(0) + pub(super) type GetTdxReport = + ioc!(TDX_CMD_GET_REPORT0, b'T', 0x01, InOutData); } /// Gets the TDX report given the specified data in `inblob`. -/// -/// The first `size_of::()` bytes of data in the returned `USegment` is the report. -/// The rest in `USegment` should be ignored. -fn tdx_get_report(inblob: &[u8]) -> Result { +fn tdx_get_report(inblob: &[u8]) -> Result> { if inblob.len() != size_of::() { return_errno_with_message!(Errno::EINVAL, "Invalid inblob length"); } @@ -321,7 +319,7 @@ fn tdx_get_report(inblob: &[u8]) -> Result { // FIXME: The `get_report` API from the `tdx_guest` crate should have been marked `unsafe` // because it has no way to determine if the input physical address is safe or not. get_report(report.paddr() as u64, report_data_paddr as u64)?; - Ok(report) + Ok(SafePtr::new(report, 0)) } fn alloc_dma_buf(buf_len: usize) -> Result { diff --git a/kernel/src/device/pty/file.rs b/kernel/src/device/pty/file.rs index e30a60bf2..3a4327026 100644 --- a/kernel/src/device/pty/file.rs +++ b/kernel/src/device/pty/file.rs @@ -7,10 +7,11 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{InodeIo, IoctlCmd, StatusFlags}, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::RawIoctl, }; /// The file for a pseudoterminal slave. @@ -81,7 +82,7 @@ impl InodeIo for PtySlaveFile { #[inherit_methods(from = "self.0")] impl FileIo for PtySlaveFile { - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result; fn check_seekable(&self) -> Result<()> { return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY"); diff --git a/kernel/src/device/pty/master.rs b/kernel/src/device/pty/master.rs index 62e0c1e12..4d6e2a421 100644 --- a/kernel/src/device/pty/master.rs +++ b/kernel/src/device/pty/master.rs @@ -2,11 +2,10 @@ use alloc::format; -use ostd::{mm::VmIo, task::Task}; +use ostd::task::Task; use super::{driver::PtyDriver, PtySlave}; use crate::{ - current_userspace, device::tty::TtyFlags, events::IoEvents, fs::{ @@ -14,7 +13,7 @@ use crate::{ file_table::FdFlags, fs_resolver::FsPath, inode_handle::FileIo, - utils::{mkmod, AccessMode, InodeIo, IoctlCmd, OpenArgs, StatusFlags}, + utils::{mkmod, AccessMode, InodeIo, OpenArgs, StatusFlags}, }, prelude::*, process::{ @@ -22,6 +21,7 @@ use crate::{ signal::{PollHandle, Pollable}, Terminal, }, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; const IO_CAPACITY: usize = 4096; @@ -135,6 +135,17 @@ impl InodeIo for PtyMaster { } } +mod ioctl_defs { + use crate::util::ioctl::{ioc, InData, NoData, OutData}; + + // Reference: + + pub(super) type SetPtyLock = ioc!(TIOCSPTLCK, b'T', 0x31, InData); + pub(super) type GetPtyLock = ioc!(TIOCGPTLCK, b'T', 0x39, OutData); + + pub(super) type OpenPtySlave = ioc!(TIOCGPTPEER, b'T', 0x41, NoData); +} + impl FileIo for PtyMaster { fn check_seekable(&self) -> Result<()> { return_errno_with_message!(Errno::ESPIPE, "the inode is a pty"); @@ -144,33 +155,37 @@ impl FileIo for PtyMaster { false } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - match cmd { - IoctlCmd::TCGETS - | IoctlCmd::TCSETS - | IoctlCmd::TCSETSW - | IoctlCmd::TCSETSF - | IoctlCmd::TIOCGWINSZ - | IoctlCmd::TIOCSWINSZ - | IoctlCmd::TIOCGPTN => return self.slave.ioctl(cmd, arg), - IoctlCmd::TIOCSPTLCK => { - let val = current_userspace!().read_val::(arg)?; + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { + use ioctl_defs::*; + + use crate::{device::tty::ioctl_defs::*, fs::utils::ioctl_defs::GetNumBytesToRead}; + + dispatch_ioctl!(match raw_ioctl { + GetTermios | SetTermios | SetTermiosDrain | SetTermiosFlush | GetWinSize + | SetWinSize | GetPtyNumber => { + return self.slave.ioctl(raw_ioctl); + } + + cmd @ SetPtyLock => { + let should_lock = cmd.read()? != 0; + let flags = self.master_flags(); - if val == 0 { - flags.clear_pty_locked(); - } else { + if should_lock { flags.set_pty_locked(); + } else { + flags.clear_pty_locked(); } } - IoctlCmd::TIOCGPTLCK => { - let val = if self.master_flags().is_pty_locked() { + cmd @ GetPtyLock => { + let is_locked = if self.master_flags().is_pty_locked() { 1 } else { 0 }; - current_userspace!().write_val(arg, &val)?; + + cmd.write(&is_locked)?; } - IoctlCmd::TIOCGPTPEER => { + _cmd @ OpenPtySlave => { let current_task = Task::current().unwrap(); let thread_local = current_task.as_thread_local().unwrap(); @@ -203,12 +218,14 @@ impl FileIo for PtyMaster { }; return Ok(fd); } - IoctlCmd::FIONREAD => { + cmd @ GetNumBytesToRead => { let len = self.slave.driver().buffer_len() as i32; - current_userspace!().write_val(arg, &len)?; + + cmd.write(&len)?; } - _ => (self.slave.clone() as Arc).job_ioctl(cmd, arg, true)?, - } + + _ => (self.slave.clone() as Arc).job_ioctl(raw_ioctl, true)?, + }); Ok(0) } diff --git a/kernel/src/device/tty/ioctl_defs.rs b/kernel/src/device/tty/ioctl_defs.rs new file mode 100644 index 000000000..364a03eb1 --- /dev/null +++ b/kernel/src/device/tty/ioctl_defs.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::{ + termio::{CTermios, CWinSize}, + CFontOp, +}; +use crate::util::ioctl::{ioc, InData, OutData, PassByVal}; + +// Reference: + +pub type GetTermios = ioc!(TCGETS, 0x5401, OutData); +pub type SetTermios = ioc!(TCSETS, 0x5402, InData); +pub type SetTermiosDrain = ioc!(TCSETSW, 0x5403, InData); +pub type SetTermiosFlush = ioc!(TCSETSF, 0x5404, InData); + +pub type GetWinSize = ioc!(TIOCGWINSZ, 0x5413, OutData); +pub type SetWinSize = ioc!(TIOCSWINSZ, 0x5414, InData); + +// TODO: Consider moving this to the `pty` module. +pub type GetPtyNumber = ioc!(TIOCGPTN, b'T', 0x30, OutData); + +pub type SetGraphicsMode = ioc!(KDSETMODE, 0x4B3A, InData); +pub type GetGraphicsMode = ioc!(KDGETMODE, 0x4B3B, OutData); + +pub type GetKeyboardMode = ioc!(KDGKBMODE, 0x4B44, OutData); +pub type SetKeyboardMode = ioc!(KDSKBMODE, 0x4B45, InData); + +pub type SetOrGetFont = ioc!(KDFONTOP, 0x4B72, InData); diff --git a/kernel/src/device/tty/mod.rs b/kernel/src/device/tty/mod.rs index 87e50157f..a04deb559 100644 --- a/kernel/src/device/tty/mod.rs +++ b/kernel/src/device/tty/mod.rs @@ -15,7 +15,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::{IoctlCmd, StatusFlags}, + utils::StatusFlags, }, prelude::*, process::{ @@ -23,11 +23,13 @@ use crate::{ signal::{PollHandle, Pollable, Pollee}, JobControl, Terminal, }, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; mod device; mod driver; mod flags; +pub(super) mod ioctl_defs; mod line_discipline; mod n_tty; mod termio; @@ -300,27 +302,31 @@ impl Tty { Ok(len) } - pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - match cmd { - IoctlCmd::TCGETS => { + pub fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { + use ioctl_defs::*; + + use crate::fs::utils::ioctl_defs::GetNumBytesToRead; + + dispatch_ioctl!(match raw_ioctl { + cmd @ GetTermios => { let termios = *self.ldisc.lock().termios(); - current_userspace!().write_val(arg, &termios)?; + cmd.write(&termios)?; } - IoctlCmd::TCSETS => { - let termios = current_userspace!().read_val(arg)?; + cmd @ SetTermios => { + let termios = cmd.read()?; self.ldisc.lock().set_termios(termios); } - IoctlCmd::TCSETSW => { - let termios = current_userspace!().read_val(arg)?; + cmd @ SetTermiosDrain => { + let termios = cmd.read()?; let mut ldisc = self.ldisc.lock(); ldisc.set_termios(termios); self.driver.drain_output(); } - IoctlCmd::TCSETSF => { - let termios = current_userspace!().read_val(arg)?; + cmd @ SetTermiosFlush => { + let termios = cmd.read()?; let mut ldisc = self.ldisc.lock(); ldisc.set_termios(termios); @@ -329,66 +335,67 @@ impl Tty { self.pollee.invalidate(); } - IoctlCmd::TIOCGWINSZ => { + cmd @ GetWinSize => { let winsize = self.ldisc.lock().window_size(); - current_userspace!().write_val(arg, &winsize)?; + cmd.write(&winsize)?; } - IoctlCmd::TIOCSWINSZ => { - let winsize = current_userspace!().read_val(arg)?; + cmd @ SetWinSize => { + let winsize = cmd.read()?; self.ldisc.lock().set_window_size(winsize); } - IoctlCmd::TIOCGPTN => { + cmd @ GetPtyNumber => { let idx = self.index; - current_userspace!().write_val(arg, &idx)?; + cmd.write(&idx)?; } - IoctlCmd::FIONREAD => { + cmd @ GetNumBytesToRead => { if self.tty_flags.is_other_closed() { return_errno_with_message!(Errno::EIO, "the TTY is closed"); } - let buffer_len = self.ldisc.lock().buffer_len() as u32; + let buffer_len = self.ldisc.lock().buffer_len() as i32; - current_userspace!().write_val(arg, &buffer_len)?; + cmd.write(&buffer_len)?; } - IoctlCmd::KDFONTOP => { - let font_op = current_userspace!().read_val(arg)?; + cmd @ SetOrGetFont => { + let font_op = cmd.read()?; self.handle_set_font(&font_op)?; } - IoctlCmd::KDSETMODE => { + cmd @ SetGraphicsMode => { let console = self.console()?; - let mode = ConsoleMode::try_from(arg as i32)?; + let mode = ConsoleMode::try_from(cmd.get())?; if !console.set_mode(mode) { return_errno_with_message!(Errno::EINVAL, "the console mode is not supported"); } } - IoctlCmd::KDGETMODE => { + cmd @ GetGraphicsMode => { let console = self.console()?; let mode = console.mode().unwrap_or(ConsoleMode::Text); - current_userspace!().write_val(arg, &(mode as i32))?; + cmd.write(&(mode as i32))?; } - IoctlCmd::KDSKBMODE => { + cmd @ SetKeyboardMode => { let console = self.console()?; - let mode = KeyboardMode::try_from(arg as i32)?; + let mode = KeyboardMode::try_from(cmd.get())?; if !console.set_keyboard_mode(mode) { return_errno_with_message!(Errno::EINVAL, "the keyboard mode is not supported"); } } - IoctlCmd::KDGKBMODE => { + cmd @ GetKeyboardMode => { let console = self.console()?; let mode = console.keyboard_mode().unwrap_or(KeyboardMode::Xlate); - current_userspace!().write_val(arg, &(mode as i32))?; + cmd.write(&(mode as i32))?; } + _ => (self.weak_self.upgrade().unwrap() as Arc) - .job_ioctl(cmd, arg, false)?, - } + .job_ioctl(raw_ioctl, false)?, + }); Ok(0) } diff --git a/kernel/src/device/tty/n_tty.rs b/kernel/src/device/tty/n_tty.rs index 50cdd5cda..374fedcb9 100644 --- a/kernel/src/device/tty/n_tty.rs +++ b/kernel/src/device/tty/n_tty.rs @@ -14,10 +14,11 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{InodeIo, IoctlCmd, StatusFlags}, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::RawIoctl, }; /// The driver for VT (virtual terminal) devices. @@ -131,7 +132,7 @@ impl InodeIo for TtyFile { #[inherit_methods(from = "self.0")] impl FileIo for TtyFile { - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result; fn check_seekable(&self) -> Result<()> { return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY"); diff --git a/kernel/src/device/tty/termio.rs b/kernel/src/device/tty/termio.rs index 427b64842..630cb2a69 100644 --- a/kernel/src/device/tty/termio.rs +++ b/kernel/src/device/tty/termio.rs @@ -230,7 +230,7 @@ impl CCtrlCharId { /// Reference: . #[derive(Debug, Clone, Copy, Pod)] #[repr(C)] -pub(super) struct CTermios { +pub struct CTermios { c_iflags: CInputFlags, c_oflags: COutputFlags, c_cflags: CCtrlFlags, @@ -321,7 +321,7 @@ impl CTermios { /// Reference: . #[derive(Debug, Clone, Copy, Default, Pod)] #[repr(C)] -pub(super) struct CWinSize { +pub struct CWinSize { ws_row: u16, ws_col: u16, ws_xpixel: u16, @@ -333,7 +333,7 @@ pub(super) struct CWinSize { /// Reference: . #[derive(Debug, Clone, Copy, Default, Pod)] #[repr(C)] -pub(super) struct CFontOp { +pub struct CFontOp { pub(super) op: u32, pub(super) flags: u32, pub(super) width: u32, diff --git a/kernel/src/fs/epoll/file.rs b/kernel/src/fs/epoll/file.rs index 5c3083710..771d7e3ea 100644 --- a/kernel/src/fs/epoll/file.rs +++ b/kernel/src/fs/epoll/file.rs @@ -17,13 +17,14 @@ use crate::{ file_table::{get_file_fast, FdFlags, FileDesc}, path::RESERVED_MOUNT_ID, pseudofs::anon_inodefs_shared_inode, - utils::{CreationFlags, Inode, IoctlCmd}, + utils::{CreationFlags, Inode}, }, prelude::*, process::{ posix_thread::ThreadLocal, signal::{PollHandle, Pollable}, }, + util::ioctl::RawIoctl, }; /// A file-like object that provides epoll API. @@ -264,8 +265,8 @@ impl FileLike for EpollFile { return_errno_with_message!(Errno::EINVAL, "epoll files do not support write"); } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { - return_errno_with_message!(Errno::EINVAL, "epoll files do not support ioctl"); + fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result { + return_errno_with_message!(Errno::ENOTTY, "epoll files do not support ioctl"); } fn inode(&self) -> &Arc { diff --git a/kernel/src/fs/file_handle.rs b/kernel/src/fs/file_handle.rs index 40f131f0a..a00bffa04 100644 --- a/kernel/src/fs/file_handle.rs +++ b/kernel/src/fs/file_handle.rs @@ -12,11 +12,12 @@ use super::{inode_handle::InodeHandle, path::Path}; use crate::{ fs::{ file_table::FdFlags, - utils::{AccessMode, FallocMode, Inode, IoctlCmd, SeekFrom, StatusFlags}, + utils::{AccessMode, FallocMode, Inode, SeekFrom, StatusFlags}, }, net::socket::Socket, prelude::*, process::signal::Pollable, + util::ioctl::RawIoctl, }; /// The basic operations defined on a file @@ -50,7 +51,7 @@ pub trait FileLike: Pollable + Send + Sync + Any { return_errno_with_message!(Errno::ESPIPE, "write_at is not supported"); } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { // `ENOTTY` means that "The specified operation does not apply to the kind of object that // the file descriptor references". // Reference: . diff --git a/kernel/src/fs/inode_handle/dyn_cap.rs b/kernel/src/fs/inode_handle/dyn_cap.rs index ce455b975..c23268e90 100644 --- a/kernel/src/fs/inode_handle/dyn_cap.rs +++ b/kernel/src/fs/inode_handle/dyn_cap.rs @@ -14,11 +14,12 @@ use crate::{ path::Path, utils::{ AccessMode, CreationFlags, DirentVisitor, FallocMode, FlockItem, Inode, InodeType, - IoctlCmd, RangeLockItem, RangeLockType, SeekFrom, StatusFlags, + RangeLockItem, RangeLockType, SeekFrom, StatusFlags, }, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::RawIoctl, }; pub struct InodeHandle(HandleInner, Rights); @@ -157,11 +158,11 @@ impl FileLike for InodeHandle { self.0.write_at(offset, reader) } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { if self.1.is_empty() { return_errno_with_message!(Errno::EBADF, "the file is opened as a path"); } - self.0.ioctl(cmd, arg) + self.0.ioctl(raw_ioctl) } fn mappable(&self) -> Result { diff --git a/kernel/src/fs/inode_handle/mod.rs b/kernel/src/fs/inode_handle/mod.rs index 7d71d8ee7..8a1f8463f 100644 --- a/kernel/src/fs/inode_handle/mod.rs +++ b/kernel/src/fs/inode_handle/mod.rs @@ -15,12 +15,13 @@ use crate::{ file_handle::Mappable, path::Path, utils::{ - DirentVisitor, FallocMode, FileRange, FlockItem, FlockList, Inode, InodeType, IoctlCmd, + DirentVisitor, FallocMode, FileRange, FlockItem, FlockList, Inode, InodeType, RangeLockItem, RangeLockList, RangeLockType, SeekFrom, StatusFlags, OFFSET_MAX, }, }, prelude::*, process::signal::{PollHandle, Pollable}, + util::ioctl::RawIoctl, }; struct HandleInner { @@ -186,9 +187,9 @@ impl HandleInner { ) } - pub(self) fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { + pub(self) fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { if let Some(ref file_io) = self.file_io { - return file_io.ioctl(cmd, arg); + return file_io.ioctl(raw_ioctl); } return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); @@ -318,7 +319,7 @@ pub trait FileIo: Pollable + InodeIo + Send + Sync + 'static { return_errno_with_message!(Errno::EINVAL, "the file is not mappable"); } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { + fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result { return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } } diff --git a/kernel/src/fs/notify/inotify.rs b/kernel/src/fs/notify/inotify.rs index 7cf5ba751..1ea0bfadc 100644 --- a/kernel/src/fs/notify/inotify.rs +++ b/kernel/src/fs/notify/inotify.rs @@ -14,12 +14,11 @@ use core::{ use bitflags::bitflags; use hashbrown::HashMap; use ostd::{ - mm::{VmIo, VmWriter}, + mm::VmWriter, sync::{Mutex, SpinLock}, }; use crate::{ - current_userspace, events::IoEvents, fs::{ file_handle::FileLike, @@ -27,11 +26,12 @@ use crate::{ notify::{FsEventSubscriber, FsEvents}, path::{Path, RESERVED_MOUNT_ID}, pseudofs::anon_inodefs_shared_inode, - utils::{AccessMode, CreationFlags, Inode, IoctlCmd, StatusFlags}, + utils::{AccessMode, CreationFlags, Inode, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable, Pollee}, return_errno_with_message, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; #[derive(Clone)] @@ -347,16 +347,18 @@ impl FileLike for InotifyFile { } } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - match cmd { - IoctlCmd::FIONREAD => { - let size = self.get_all_event_size(); - let size_addr = arg; - current_userspace!().write_val(size_addr, &size)?; + fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { + use crate::fs::utils::ioctl_defs::GetNumBytesToRead; + + dispatch_ioctl!(match raw_ioctl { + cmd @ GetNumBytesToRead => { + let size = self.get_all_event_size() as i32; + + cmd.write(&size)?; Ok(0) } - _ => return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"), - } + _ => return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"), + }) } fn status_flags(&self) -> StatusFlags { diff --git a/kernel/src/fs/ramfs/memfd.rs b/kernel/src/fs/ramfs/memfd.rs index cc35dd7b2..9ed89d0f5 100644 --- a/kernel/src/fs/ramfs/memfd.rs +++ b/kernel/src/fs/ramfs/memfd.rs @@ -27,8 +27,8 @@ use crate::{ tmpfs::TmpFs, utils::{ chmod, mkmod, AccessMode, CachePage, CreationFlags, Extension, FallocMode, FileSystem, - Inode, InodeIo, InodeMode, InodeType, IoctlCmd, Metadata, OpenArgs, PageCacheBackend, - SeekFrom, StatusFlags, XattrName, XattrNamespace, XattrSetFlags, + Inode, InodeIo, InodeMode, InodeType, Metadata, OpenArgs, PageCacheBackend, SeekFrom, + StatusFlags, XattrName, XattrNamespace, XattrSetFlags, }, }, prelude::*, @@ -36,6 +36,7 @@ use crate::{ signal::{PollHandle, Pollable}, Gid, Uid, }, + util::ioctl::RawIoctl, vm::{perms::VmPerms, vmo::Vmo}, }; @@ -462,7 +463,7 @@ impl FileLike for MemfdFile { Ok(Mappable::Inode(self.memfd_inode.clone())) } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { + fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result { if self.rights.is_empty() { return_errno_with_message!(Errno::EBADF, "the file is opened as a path"); } diff --git a/kernel/src/fs/utils/ioctl_defs.rs b/kernel/src/fs/utils/ioctl_defs.rs new file mode 100644 index 000000000..a83d3cdff --- /dev/null +++ b/kernel/src/fs/utils/ioctl_defs.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::util::ioctl::{ioc, OutData}; + +// Reference: + +pub type GetNumBytesToRead = ioc!(FIONREAD, 0x541B, OutData); diff --git a/kernel/src/fs/utils/mod.rs b/kernel/src/fs/utils/mod.rs index 03ba8bc0d..f478a259e 100644 --- a/kernel/src/fs/utils/mod.rs +++ b/kernel/src/fs/utils/mod.rs @@ -39,6 +39,7 @@ mod fs; mod id_bitmap; mod inode; mod inode_mode; +pub mod ioctl_defs; mod open_args; mod page_cache; mod random_test; diff --git a/kernel/src/process/process/terminal.rs b/kernel/src/process/process/terminal.rs index 33d68913d..29711a7f9 100644 --- a/kernel/src/process/process/terminal.rs +++ b/kernel/src/process/process/terminal.rs @@ -2,14 +2,12 @@ use alloc::sync::Arc; -use ostd::mm::VmIo; - -use super::{session::SessionGuard, JobControl, Pgid, Process, Session, Sid}; +use super::{session::SessionGuard, JobControl, Pgid, Process, Session}; use crate::{ - current_userspace, - fs::{device::Device, utils::IoctlCmd}, + fs::device::Device, prelude::{current, return_errno_with_message, warn, Errno, Error, Result}, process::process_table, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; /// A terminal. @@ -21,19 +19,29 @@ pub trait Terminal: Device { fn job_control(&self) -> &JobControl; } -impl dyn Terminal { - pub fn job_ioctl(self: Arc, cmd: IoctlCmd, arg: usize, via_master: bool) -> Result<()> { - match cmd { - // Commands about foreground process groups - IoctlCmd::TIOCSPGRP => { - let pgid = current_userspace!().read_val::(arg)?; - if pgid.cast_signed() < 0 { - return_errno_with_message!(Errno::EINVAL, "negative PGIDs are not valid"); - } +mod ioctl_defs { + use crate::{ + process::{Pgid, Sid}, + util::ioctl::{ioc, InData, NoData, OutData, PassByVal}, + }; - self.set_foreground(pgid, ¤t!()) - } - IoctlCmd::TIOCGPGRP => { + // Reference: + + pub(super) type GetForegroundPgid = ioc!(TIOCGPGRP, 0x540F, OutData); + pub(super) type SetForegroundPgid = ioc!(TIOCSPGRP, 0x5410, InData); + + pub(super) type SetControlTty = ioc!(TIOCSCTTY, 0x540E, InData); + pub(super) type SetControlNoTty = ioc!(TIOCNOTTY, 0x5422, NoData); + pub(super) type GetControlSid = ioc!(TIOCGSID, 0x5429, OutData); +} + +impl dyn Terminal { + pub fn job_ioctl(self: Arc, raw_ioctl: RawIoctl, via_master: bool) -> Result<()> { + use ioctl_defs::*; + + dispatch_ioctl!(match raw_ioctl { + // Commands about foreground process groups + cmd @ GetForegroundPgid => { let operate = || { self.job_control() .foreground() @@ -46,18 +54,26 @@ impl dyn Terminal { self.is_control_and(¤t!(), |_, _| Ok(operate()))? }; - Ok(current_userspace!().write_val::(arg, &pgid)?) + cmd.write(&pgid) + } + cmd @ SetForegroundPgid => { + let pgid = cmd.read()?; + if pgid.cast_signed() < 0 { + return_errno_with_message!(Errno::EINVAL, "negative PGIDs are not valid"); + } + + self.set_foreground(pgid, ¤t!()) } // Commands about sessions - IoctlCmd::TIOCSCTTY => { - if arg == 1 { + cmd @ SetControlTty => { + if cmd.get() == 1 { warn!("stealing TTY from another session is not supported"); } self.set_control(¤t!()) } - IoctlCmd::TIOCNOTTY => { + _cmd @ SetControlNoTty => { if via_master { return_errno_with_message!( Errno::ENOTTY, @@ -67,7 +83,7 @@ impl dyn Terminal { self.unset_control(¤t!()) } - IoctlCmd::TIOCGSID => { + cmd @ GetControlSid => { let sid = if via_master { self.job_control() .session() @@ -82,14 +98,14 @@ impl dyn Terminal { self.is_control_and(¤t!(), |session, _| Ok(session.sid()))? }; - Ok(current_userspace!().write_val::(arg, &sid)?) + cmd.write(&sid) } // Commands that are invalid or not supported _ => { - return_errno_with_message!(Errno::EINVAL, "the `ioctl` command is invalid") + return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown") } - } + }) } /// Sets the terminal to be the controlling terminal of the process. diff --git a/kernel/src/syscall/ioctl.rs b/kernel/src/syscall/ioctl.rs index bac176af4..7594779ab 100644 --- a/kernel/src/syscall/ioctl.rs +++ b/kernel/src/syscall/ioctl.rs @@ -1,73 +1,121 @@ // SPDX-License-Identifier: MPL-2.0 -use ostd::mm::VmIo; - use super::SyscallReturn; use crate::{ fs::{ + file_handle::FileLike, file_table::{get_file_fast, FdFlags, FileDesc, WithFileTable}, - utils::{IoctlCmd, StatusFlags}, + utils::StatusFlags, }, prelude::*, + process::posix_thread::FileTableRefMut, + util::ioctl::{dispatch_ioctl, RawIoctl}, }; pub fn sys_ioctl(fd: FileDesc, cmd: u32, arg: Vaddr, ctx: &Context) -> Result { - let ioctl_cmd = IoctlCmd::try_from(cmd)?; - debug!( - "fd = {}, ioctl_cmd = {:?}, arg = 0x{:x}", - fd, ioctl_cmd, arg - ); + let raw_ioctl = RawIoctl::new(cmd, arg); + debug!("fd = {}, raw_ioctl = {:#x?}", fd, raw_ioctl,); let mut file_table = ctx.thread_local.borrow_file_table_mut(); + + // First, handle the ioctl command that affects the file descriptor. + if let Some(res) = handle_fd_ioctl(&mut file_table, fd, raw_ioctl) { + res?; + return Ok(SyscallReturn::Return(0)); + } + let file = get_file_fast!(&mut file_table, fd); - let res = match ioctl_cmd { - IoctlCmd::FIONBIO => { - let is_nonblocking = ctx.user_space().read_val::(arg)? != 0; - let mut flags = file.status_flags(); - flags.set(StatusFlags::O_NONBLOCK, is_nonblocking); - file.set_status_flags(flags)?; - 0 - } - IoctlCmd::FIOASYNC => { - let is_async = ctx.user_space().read_val::(arg)? != 0; - let mut flags = file.status_flags(); - // Set `O_ASYNC` flags will send `SIGIO` signal to a process when - // I/O is possible, user should call `fcntl(fd, F_SETOWN, pid)` - // first to let the kernel know just whom to notify. - flags.set(StatusFlags::O_ASYNC, is_async); - file.set_status_flags(flags)?; - 0 - } - IoctlCmd::FIOCLEX => { - // Sets the close-on-exec flag of the file. - // Follow the implementation of fcntl() - - file_table.read_with(|inner| { - let entry = inner.get_entry(fd)?; - entry.set_flags(entry.flags() | FdFlags::CLOEXEC); - Ok::<_, Error>(0) - })? - } - IoctlCmd::FIONCLEX => { - // Clears the close-on-exec flag of the file. - // Follow the implementation of fcntl() - - file_table.read_with(|inner| { - let entry = inner.get_entry(fd)?; - entry.set_flags(entry.flags() - FdFlags::CLOEXEC); - Ok::<_, Error>(0) - })? - } - // FIXME: ioctl operations involving blocking I/O should be able to restart if interrupted - _ => { - let file_owned = file.into_owned(); - // We have to drop `file_table` because some I/O command will modify the file table - // (e.g., TIOCGPTPEER). - drop(file_table); - - file_owned.ioctl(ioctl_cmd, arg)? - } + // Then, handle the ioctl command the affects the file description. + let res = if let Some(res) = handle_file_ioctl(&**file, raw_ioctl) { + res?; + 0 + } else { + let file_owned = file.into_owned(); + // We have to drop `file_table` because some I/O command will modify the file table + // (e.g., TIOCGPTPEER). + drop(file_table); + file_owned.ioctl(raw_ioctl)? }; + Ok(SyscallReturn::Return(res as _)) } + +mod ioctl_defs { + use crate::util::ioctl::{ioc, InData, NoData}; + + // Reference: + + pub(super) type SetNonBlocking = ioc!(FIONBIO, 0x5421, InData); + pub(super) type SetAsync = ioc!(FIOASYNC, 0x5452, InData); + + pub(super) type SetNotCloseOnExec = ioc!(FIONCLEX, 0x5450, NoData); + pub(super) type SetCloseOnExec = ioc!(FIOCLEX, 0x5451, NoData); +} + +fn handle_fd_ioctl( + file_table: &mut FileTableRefMut, + fd: FileDesc, + raw_ioctl: RawIoctl, +) -> Option> { + use ioctl_defs::*; + + dispatch_ioctl!(match raw_ioctl { + SetNotCloseOnExec => { + // Clears the close-on-exec flag of the file. + // Follow the implementation of `fcntl()`. + + Some(file_table.read_with(|inner| { + let entry = inner.get_entry(fd)?; + // FIXME: This is racy. + entry.set_flags(entry.flags() - FdFlags::CLOEXEC); + Ok(()) + })) + } + SetCloseOnExec => { + // Sets the close-on-exec flag of the file. + // Follow the implementation of `fcntl()`. + + Some(file_table.read_with(|inner| { + let entry = inner.get_entry(fd)?; + // FIXME: This is racy. + entry.set_flags(entry.flags() | FdFlags::CLOEXEC); + Ok(()) + })) + } + _ => None, + }) +} + +fn handle_file_ioctl(file: &dyn FileLike, raw_ioctl: RawIoctl) -> Option> { + use ioctl_defs::*; + + dispatch_ioctl!(match raw_ioctl { + cmd @ SetNonBlocking => { + let handler = || { + let is_nonblocking = cmd.read()? != 0; + + let mut flags = file.status_flags(); + flags.set(StatusFlags::O_NONBLOCK, is_nonblocking); + // FIXME: This is racy. + file.set_status_flags(flags) + }; + Some(handler()) + } + cmd @ SetAsync => { + let handler = || { + let is_async = cmd.read()? != 0; + + // Setting the `O_ASYNC` flag will cause the kernel to send the owner process a + // `SIGIO` signal when input/output is possible. The user should first call + // `fcntl(fd, F_SETOWN, pid)` to specify the process to be notified. + let mut flags = file.status_flags(); + flags.set(StatusFlags::O_ASYNC, is_async); + // FIXME: This is racy. + file.set_status_flags(flags) + }; + Some(handler()) + } + _ => None, + }) +}