Migrate to new ioctl infrastructure

This commit is contained in:
Ruihan Li 2025-12-04 23:06:55 +08:00 committed by Tate, Hongliang Tian
parent 99fefb7adf
commit 4e76ed5fea
20 changed files with 450 additions and 272 deletions

View File

@ -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<T, M: HasPaddr, R> HasPaddr for SafePtr<T, M, R> {
// =============== Read and write methods ==============
impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
/// Read the value from the pointer.
/// Reads the value from the pointer.
///
/// # Access rights
///
@ -190,7 +193,7 @@ impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
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<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
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<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
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<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
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<M1: HasVmReaderWriter, R1: TRights>(
&self,
ptr: &SafePtr<T, M1, TRightSet<R1>>,
) -> Result<()> {
let mut reader = M1::Types::to_reader_result(ptr.vm_obj.reader())?.to_fallible();
if reader.remain() < size_of::<T>() {
return Err(Error::InvalidArgs);
}
reader.limit(size_of::<T>());
self.vm_obj.write(self.offset, &mut reader)
}
}
// =============== Read and write methods ==============

View File

@ -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<i32> {
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
// TODO: Support ioctl operations for evdev files.
// Most ioctl implementations return `ENOTTY` for invalid ioctl commands, representing "The

View File

@ -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: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/fb.h#L13-L38>
pub(super) type GetVarScreenInfo = ioc!(FBIOGET_VSCREENINFO, 0x4600, OutData<FbVarScreenInfo>);
pub(super) type PutVarScreenInfo = ioc!(FBIOPUT_VSCREENINFO, 0x4601, InOutData<FbVarScreenInfo>);
pub(super) type GetFixScreenInfo = ioc!(FBIOGET_FSCREENINFO, 0x4602, OutData<FbFixScreenInfo>);
pub(super) type GetColorMap = ioc!(FBIOGETCMAP, 0x4604, InData<FbCmapUser>);
pub(super) type PutColorMap = ioc!(FBIOPUTCMAP, 0x4605, InData<FbCmapUser>);
// `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<i32> {
/// 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<i32> {
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<i32> {
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<i32> {
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<i32> {
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<i32> {
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: <https://elixir.bootlin.com/linux/v6.17/source/drivers/video/fbdev/core/fbmem.c#L276-L279>.
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");
}
}
})
}
}

View File

@ -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<i32> {
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<i32> {
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<Box<[u8]>> {
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<TdxQuoteHdr, _, _> = SafePtr::new(&buf, 0);
let payload_ptr: SafePtr<TdReport, _, _> = SafePtr::new(&buf, size_of::<TdxQuoteHdr>());
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::<TdReport>() as u32))?;
field_ptr!(&report_ptr, TdxQuoteHdr, out_len).write(&0u32)?;
buf.write(
size_of::<TdxQuoteHdr>(),
report.reader().to_fallible().limit(size_of::<TdReport>()),
)?;
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<Box<[u8]>> {
// "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::<TdxQuoteHdr>(), 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<i32> {
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: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/tdx-guest.h#L40>
let tdx_report_vaddr = arg + offset_of!(TdxReportRequest, tdx_report);
user_space.write_bytes(
tdx_report_vaddr,
report.reader().limit(size_of::<TdReport>()),
)?;
Ok(0)
pub(super) type GetTdxReport =
ioc!(TDX_CMD_GET_REPORT0, b'T', 0x01, InOutData<TdxReportRequest>);
}
/// Gets the TDX report given the specified data in `inblob`.
///
/// The first `size_of::<TdReport>()` bytes of data in the returned `USegment` is the report.
/// The rest in `USegment` should be ignored.
fn tdx_get_report(inblob: &[u8]) -> Result<USegment> {
fn tdx_get_report(inblob: &[u8]) -> Result<SafePtr<TdReport, USegment>> {
if inblob.len() != size_of::<ReportData>() {
return_errno_with_message!(Errno::EINVAL, "Invalid inblob length");
}
@ -321,7 +319,7 @@ fn tdx_get_report(inblob: &[u8]) -> Result<USegment> {
// 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<DmaCoherent> {

View File

@ -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<i32>;
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
fn check_seekable(&self) -> Result<()> {
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");

View File

@ -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: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
pub(super) type SetPtyLock = ioc!(TIOCSPTLCK, b'T', 0x31, InData<i32>);
pub(super) type GetPtyLock = ioc!(TIOCGPTLCK, b'T', 0x39, OutData<i32>);
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<i32> {
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::<i32>(arg)?;
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
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<dyn Terminal>).job_ioctl(cmd, arg, true)?,
}
_ => (self.slave.clone() as Arc<dyn Terminal>).job_ioctl(raw_ioctl, true)?,
});
Ok(0)
}

View File

@ -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: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
pub type GetTermios = ioc!(TCGETS, 0x5401, OutData<CTermios>);
pub type SetTermios = ioc!(TCSETS, 0x5402, InData<CTermios>);
pub type SetTermiosDrain = ioc!(TCSETSW, 0x5403, InData<CTermios>);
pub type SetTermiosFlush = ioc!(TCSETSF, 0x5404, InData<CTermios>);
pub type GetWinSize = ioc!(TIOCGWINSZ, 0x5413, OutData<CWinSize>);
pub type SetWinSize = ioc!(TIOCSWINSZ, 0x5414, InData<CWinSize>);
// TODO: Consider moving this to the `pty` module.
pub type GetPtyNumber = ioc!(TIOCGPTN, b'T', 0x30, OutData<u32>);
pub type SetGraphicsMode = ioc!(KDSETMODE, 0x4B3A, InData<i32, PassByVal>);
pub type GetGraphicsMode = ioc!(KDGETMODE, 0x4B3B, OutData<i32>);
pub type GetKeyboardMode = ioc!(KDGKBMODE, 0x4B44, OutData<i32>);
pub type SetKeyboardMode = ioc!(KDSKBMODE, 0x4B45, InData<i32, PassByVal>);
pub type SetOrGetFont = ioc!(KDFONTOP, 0x4B72, InData<CFontOp>);

View File

@ -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<D: TtyDriver> Tty<D> {
Ok(len)
}
pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
match cmd {
IoctlCmd::TCGETS => {
pub fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
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<D: TtyDriver> Tty<D> {
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<dyn Terminal>)
.job_ioctl(cmd, arg, false)?,
}
.job_ioctl(raw_ioctl, false)?,
});
Ok(0)
}

View File

@ -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<D: TtyDriver> InodeIo for TtyFile<D> {
#[inherit_methods(from = "self.0")]
impl<D: TtyDriver> FileIo for TtyFile<D> {
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
fn check_seekable(&self) -> Result<()> {
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");

View File

@ -230,7 +230,7 @@ impl CCtrlCharId {
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L30>.
#[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: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termios.h#L15>.
#[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: <https://elixir.bootlin.com/linux/v6.15/source/include/uapi/linux/kd.h#L159>.
#[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,

View File

@ -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<i32> {
return_errno_with_message!(Errno::EINVAL, "epoll files do not support ioctl");
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
return_errno_with_message!(Errno::ENOTTY, "epoll files do not support ioctl");
}
fn inode(&self) -> &Arc<dyn Inode> {

View File

@ -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<i32> {
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
// `ENOTTY` means that "The specified operation does not apply to the kind of object that
// the file descriptor references".
// Reference: <https://man7.org/linux/man-pages/man2/ioctl.2.html>.

View File

@ -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<i32> {
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
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<Mappable> {

View File

@ -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<i32> {
pub(self) fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
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<i32> {
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported");
}
}

View File

@ -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<i32> {
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<i32> {
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 {

View File

@ -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<i32> {
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
if self.rights.is_empty() {
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
}

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
use crate::util::ioctl::{ioc, OutData};
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
pub type GetNumBytesToRead = ioc!(FIONREAD, 0x541B, OutData<i32>);

View File

@ -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;

View File

@ -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<Self>, cmd: IoctlCmd, arg: usize, via_master: bool) -> Result<()> {
match cmd {
// Commands about foreground process groups
IoctlCmd::TIOCSPGRP => {
let pgid = current_userspace!().read_val::<Pgid>(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, &current!())
}
IoctlCmd::TIOCGPGRP => {
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
pub(super) type GetForegroundPgid = ioc!(TIOCGPGRP, 0x540F, OutData<Pgid>);
pub(super) type SetForegroundPgid = ioc!(TIOCSPGRP, 0x5410, InData<Pgid>);
pub(super) type SetControlTty = ioc!(TIOCSCTTY, 0x540E, InData<i32, PassByVal>);
pub(super) type SetControlNoTty = ioc!(TIOCNOTTY, 0x5422, NoData);
pub(super) type GetControlSid = ioc!(TIOCGSID, 0x5429, OutData<Sid>);
}
impl dyn Terminal {
pub fn job_ioctl(self: Arc<Self>, 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(&current!(), |_, _| Ok(operate()))?
};
Ok(current_userspace!().write_val::<Pgid>(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, &current!())
}
// 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(&current!())
}
IoctlCmd::TIOCNOTTY => {
_cmd @ SetControlNoTty => {
if via_master {
return_errno_with_message!(
Errno::ENOTTY,
@ -67,7 +83,7 @@ impl dyn Terminal {
self.unset_control(&current!())
}
IoctlCmd::TIOCGSID => {
cmd @ GetControlSid => {
let sid = if via_master {
self.job_control()
.session()
@ -82,14 +98,14 @@ impl dyn Terminal {
self.is_control_and(&current!(), |session, _| Ok(session.sid()))?
};
Ok(current_userspace!().write_val::<Sid>(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.

View File

@ -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<SyscallReturn> {
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::<i32>(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::<i32>(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: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
pub(super) type SetNonBlocking = ioc!(FIONBIO, 0x5421, InData<i32>);
pub(super) type SetAsync = ioc!(FIOASYNC, 0x5452, InData<i32>);
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<Result<()>> {
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<Result<()>> {
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,
})
}