From f3b1acf3ce95ed5a013d77d1031ebe77a1972cac Mon Sep 17 00:00:00 2001 From: ClawSeven Date: Tue, 30 Apr 2024 11:41:44 +0800 Subject: [PATCH] [libos] Re-implement ioctl commands --- src/libos/src/fs/dev_fs/dev_sgx/mod.rs | 6 +- src/libos/src/fs/file.rs | 7 +- .../fs/file_ops/ioctl/builtin/get_ifconf.rs | 136 ++++++++ .../fs/file_ops/ioctl/builtin/get_ifreq.rs | 77 +++++ .../file_ops/ioctl/builtin/get_readbuflen.rs | 28 ++ .../src/fs/file_ops/ioctl/builtin/mod.rs | 310 ++++++++++++++++++ .../ioctl/builtin/set_close_on_exec.rs | 5 + .../file_ops/ioctl/builtin/set_nonblocking.rs | 24 ++ .../ioctl/{builtin.rs => builtin/termios.rs} | 119 +++---- .../src/fs/file_ops/ioctl/builtin/winsize.rs | 64 ++++ src/libos/src/fs/file_ops/ioctl/macros.rs | 34 +- src/libos/src/fs/file_ops/ioctl/mod.rs | 145 ++++++-- .../src/fs/file_ops/ioctl/non_builtin.rs | 22 ++ src/libos/src/fs/file_ops/mod.rs | 5 +- src/libos/src/fs/inode_file.rs | 18 +- src/libos/src/fs/mod.rs | 5 +- src/libos/src/fs/pipe.rs | 34 +- src/libos/src/fs/stdio.rs | 110 ++----- src/libos/src/fs/syscalls.rs | 2 +- src/libos/src/fs/timer_file.rs | 5 - 20 files changed, 906 insertions(+), 250 deletions(-) create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/get_ifconf.rs create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/get_ifreq.rs create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/get_readbuflen.rs create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/mod.rs create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/set_close_on_exec.rs create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/set_nonblocking.rs rename src/libos/src/fs/file_ops/ioctl/{builtin.rs => builtin/termios.rs} (64%) create mode 100644 src/libos/src/fs/file_ops/ioctl/builtin/winsize.rs diff --git a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs index 8aa99c6e..e258f508 100644 --- a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs +++ b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs @@ -45,7 +45,7 @@ impl INode for DevSgx { fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> { let mut ioctl_cmd = - unsafe { IoctlCmd::new(_cmd, _data as *mut u8).map_err(|_| FsError::InvalidParam)? }; + unsafe { IoctlRawCmd::new(_cmd, _data as *mut u8).map_err(|_| FsError::InvalidParam)? }; self.ioctl(&mut ioctl_cmd).map_err(|e| { error!("{}", e.backtrace()); FsError::IOCTLError @@ -59,9 +59,9 @@ impl INode for DevSgx { } impl DevSgx { - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { + fn ioctl(&self, cmd: &mut IoctlRawCmd) -> Result { let nonbuiltin_cmd = match cmd { - IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, + IoctlRawCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, _ => return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"), }; let cmd_num = nonbuiltin_cmd.cmd_num().as_u32(); diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index d7401a99..66429cfa 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -1,4 +1,5 @@ use super::*; +use crate::fs::IoctlCmd; macro_rules! return_op_unsupported_error { ($op_name: expr, $errno: expr) => {{ @@ -79,8 +80,8 @@ pub trait File: Debug + Sync + Send + Any { Ok(()) } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - return_op_unsupported_error!("ioctl") + fn ioctl(&self, _cmd: &mut dyn IoctlCmd) -> Result<()> { + return_op_unsupported_error!("ioctl", EINVAL); } fn access_mode(&self) -> Result { @@ -142,7 +143,7 @@ pub trait File: Debug + Sync + Send + Any { /// Return the host fd, if the file is backed by an underlying host file. fn host_fd(&self) -> Option<&HostFd> { - return None; + None } /// Update the ready events of a host file. diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/get_ifconf.rs b/src/libos/src/fs/file_ops/ioctl/builtin/get_ifconf.rs new file mode 100644 index 00000000..66d89a1d --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/get_ifconf.rs @@ -0,0 +1,136 @@ +use super::IoctlCmd; +use crate::prelude::*; + +#[derive(Debug)] +#[repr(C)] +pub struct IfConf { + pub ifc_len: i32, + pub ifc_buf: *const u8, +} + +#[derive(Debug)] +pub enum GetIfConf { + IfConfBuf(Vec), + IfConfLen(usize), +} + +impl GetIfConf { + pub fn new(ifconf: &IfConf) -> Self { + if ifconf.ifc_buf.is_null() { + Self::IfConfLen(ifconf.ifc_len as usize) + } else { + let buf = { + let mut buf = Vec::with_capacity(ifconf.ifc_len as usize); + buf.resize(ifconf.ifc_len as usize, 0); + buf + }; + Self::IfConfBuf(buf) + } + } + + pub fn execute(&mut self, fd: FileDesc) -> Result<()> { + if let Self::IfConfBuf(buf) = self { + if buf.len() == 0 { + return Ok(()); + } + } + + let mut if_conf = self.to_raw_ifconf(); + get_ifconf_by_host(fd, &mut if_conf)?; + self.set_len(if_conf.ifc_len as usize); + Ok(()) + } + + fn set_len(&mut self, new_len: usize) { + match self { + Self::IfConfLen(len) => { + *len = new_len; + } + Self::IfConfBuf(buf) => { + buf.resize(new_len, 0); + } + } + } + + pub fn len(&self) -> usize { + match self { + Self::IfConfLen(len) => *len, + Self::IfConfBuf(buf) => buf.len(), + } + } + + pub fn as_slice(&self) -> Option<&[u8]> { + match self { + Self::IfConfBuf(buf) => Some(buf.as_slice()), + Self::IfConfLen(_) => None, + } + } + + pub fn to_raw_ifconf(&self) -> IfConf { + match self { + Self::IfConfLen(len) => IfConf { + ifc_buf: std::ptr::null(), + ifc_len: *len as i32, + }, + Self::IfConfBuf(buf) => IfConf { + ifc_buf: buf.as_ptr(), + ifc_len: buf.len() as i32, + }, + } + } +} + +impl IoctlCmd for GetIfConf {} + +const SIOCGIFCONF: u32 = 0x8912; + +fn get_ifconf_by_host(fd: FileDesc, if_conf: &mut IfConf) -> Result<()> { + extern "C" { + // Used to ioctl arguments with pointer members. + // + // Before the call the area the pointers points to should be assembled into + // one continuous memory block. Then the block is repacked to ioctl arguments + // in the ocall implementation in host. + // + // ret: holds the return value of ioctl in host + // fd: the host fd for the device + // cmd_num: request number of the ioctl + // buf: the data to exchange with host + // len: the size of the buf + // recv_len: accepts transferred data length when buf is used to get data from host + // + fn socket_ocall_ioctl_repack( + ret: *mut i32, + fd: i32, + cmd_num: i32, + buf: *const u8, + len: i32, + recv_len: *mut i32, + ) -> sgx_types::sgx_status_t; + } + + try_libc!({ + let mut recv_len: i32 = 0; + let mut retval: i32 = 0; + let status = socket_ocall_ioctl_repack( + &mut retval as *mut i32, + fd as _, + SIOCGIFCONF as _, + if_conf.ifc_buf, + if_conf.ifc_len, + &mut recv_len as *mut i32, + ); + assert!(status == sgx_types::sgx_status_t::SGX_SUCCESS); + + // If ifc_req is NULL, SIOCGIFCONF returns the necessary buffer + // size in bytes for receiving all available addresses in ifc_len + // which is irrelevant to the orginal ifc_len. + if !if_conf.ifc_buf.is_null() { + assert!(if_conf.ifc_len >= recv_len); + } + if_conf.ifc_len = recv_len; + retval + }); + + Ok(()) +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/get_ifreq.rs b/src/libos/src/fs/file_ops/ioctl/builtin/get_ifreq.rs new file mode 100644 index 00000000..cb9b7d91 --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/get_ifreq.rs @@ -0,0 +1,77 @@ +use crate::prelude::*; + +const IFNAMSIZ: usize = 16; +use super::IoctlCmd; + +#[derive(Debug, Clone, Copy, Default)] +#[repr(C)] +pub struct IfReq { + pub ifr_name: [u8; IFNAMSIZ], + pub ifr_union: [u8; 24], +} + +/// Many of the socket ioctl commands use `IfReq` as the structure to get configuration +/// of network devices. The only difference is the command number. +/// +/// This structure wraps the `GetIfReq` and the command number as the `IoctlCmd`. +#[derive(Debug)] +pub struct GetIfReqWithRawCmd { + inner: GetIfReq, + raw_cmd: u32, +} + +impl GetIfReqWithRawCmd { + pub fn new(raw_cmd: u32, req: IfReq) -> Self { + Self { + inner: GetIfReq::new(req), + raw_cmd, + } + } + + pub fn output(&self) -> Option<&IfReq> { + self.inner.output() + } + + pub fn execute(&mut self, fd: FileDesc) -> Result<()> { + let input_if_req = self.inner.input(); + let output_if_req = get_ifreq_by_host(fd, self.raw_cmd, input_if_req)?; + self.inner.set_output(output_if_req); + Ok(()) + } +} + +fn get_ifreq_by_host(fd: FileDesc, cmd: u32, req: &IfReq) -> Result { + let mut if_req: IfReq = req.clone(); + try_libc!({ + let mut retval: i32 = 0; + extern "C" { + pub fn occlum_ocall_ioctl( + ret: *mut i32, + fd: c_int, + request: c_int, + arg: *mut c_void, + len: size_t, + ) -> sgx_types::sgx_status_t; + } + + use libc::{c_int, c_void, size_t}; + use occlum_ocall_ioctl as do_ioctl; + + let status = do_ioctl( + &mut retval as *mut i32, + fd as i32, + cmd as i32, + &mut if_req as *mut IfReq as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_types::sgx_status_t::SGX_SUCCESS); + retval + }); + Ok(if_req) +} + +impl IoctlCmd for GetIfReqWithRawCmd {} + +impl_ioctl_cmd! { + pub struct GetIfReq {} +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/get_readbuflen.rs b/src/libos/src/fs/file_ops/ioctl/builtin/get_readbuflen.rs new file mode 100644 index 00000000..258a0cf7 --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/get_readbuflen.rs @@ -0,0 +1,28 @@ +use super::*; + +impl_ioctl_cmd! { + pub struct GetReadBufLen {} +} + +impl GetReadBufLen { + pub fn execute(&mut self, host_fd: FileDesc) -> Result<()> { + let mut buflen: i32 = 0; + + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::FIONREAD as i32, + &mut buflen as *mut i32 as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + + trace!("read buf len = {:?}", buflen); + self.set_output(buflen); + Ok(()) + } +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/mod.rs b/src/libos/src/fs/file_ops/ioctl/builtin/mod.rs new file mode 100644 index 00000000..3ac75d57 --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/mod.rs @@ -0,0 +1,310 @@ +//! An ioctl-style, extensible API for file types. +//! +//! # Motiviation +//! +//! Let's consider three classic versatile APIs for files in Unix-like +//! systems: `ioctl`, `fcntl`, and `getsockopt`/`setsockopt`. +//! +//! ```c +//! int fcntl(int fd, int cmd, ... /* arg */ ); +//! +//! int ioctl(int fd, unsigned long request, ... /* arg */); +//! +//! int getsockopt(int sockfd, int level, int optname, +//! void *restrict optval, socklen_t *restrict optlen); +//! int setsockopt(int sockfd, int level, int optname, +//! const void *optval, socklen_t optlen); +//! ``` +//! +//! The three APIs have two properties in common: _extensibility_ and +//! _type erasure_. By extensibility, we mean that it is quite easy to add +//! new sub-commands that are specific to a device or file. And type erasure +//! underscores the fact that since the input and output arguments of a future +//! sub-command cannot be known beforehand, the concrete types of these +//! arguments have to be "erased" from the interface using `...` or `void*`. +//! +//! So how do we support these three C APIs in our Rust types for files? +//! +//! Specifically, do we need to add three separate Rust APIs corresponding to +//! their C counterparts? And what is the best way to express type-erased +//! arguments in the type-safe language of Rust? And most importantly, how +//! can we add new kinds of sub-commands and handle all kinds of sub-commands +//! as painless as possible? +//! +//! Our solution is the `IoctlCmd` trait and its companion macros. +//! +//! # Usage +//! +//! Here is a simple program to demonstrate the usage of `IoctlCmd`. +//! +//! ```rust +//! use net::{impl_ioctl_cmd, match_ioctl_cmd_mut}; +//! use net::ioctl::{IoctlCmd}; +//! +//! /// A trait to abstract any file-like type. +//! /// +//! /// For our purpose, it suffices for the trait to have only one API, +//! /// which takes a mutable trait object of `IoctlCmd` as its argument. +//! /// Thanks to `IoctlCmd`'s capability of downcasting itself to the +//! /// concrete type that implements the trait, it becomes easy to accept +//! /// all kinds of commands without breaking changes to the API signature. +//! pub trait File { +//! fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()>; +//! } +//! +//! // A typical command consists of an input and an output. For such commands, +//! // you can use the `impl_ioctl_cmd` to define them handily. +//! // +//! // Here, three structs are defined, implementing the `IoctlCmd` trait. +//! // Each of them represent a different command, having different +//! // input and output arguments. +//! // +//! // Note that while the trait is named `IoctlCmd`, it does not +//! // preclude using the trait to abstract `fcntl` or `getsocktopt` +//! // commands. +//! impl_ioctl_cmd! { +//! pub struct CommonCmd {} +//! } +//! +//! impl_ioctl_cmd! { +//! pub struct FooCmd {} +//! } +//! +//! impl_ioctl_cmd! { +//! pub struct BarCmd {} +//! } +//! +//! // Of course. You can implement an ioctl command manually, without +//! // using the `impl_ioctl_cmd` macro. +//! #[derive(Debug)] +//! pub struct ComplexCmd { /* customized memory layout */ }; +//! impl IoctlCmd for ComplexCmd {} +//! +//! pub struct FooFile; +//! impl File for FooFile { +//! fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { +//! // Only handle the interesting commands. The trait object +//! // is automatically downcasted to the concrete struct that +//! // represents the command. +//! match_ioctl_cmd_mut!(&mut *cmd, { +//! cmd: CommonCmd => { +//! println!("Accepted CommonCmd: {:?}", cmd); +//! Ok(()) +//! }, +//! cmd: FooCmd => { +//! println!("FooCmd's input: {}", cmd.input()); +//! Ok(()) +//! }, +//! _ => { +//! Err(errno!(EINVAL, "unknown command")) +//! } +//! }) +//! } +//! } +//! +//! pub struct BarFile; +//! impl File for BarFile { +//! fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { +//! match_ioctl_cmd_mut!(&mut *cmd, { +//! cmd: CommonCmd => { +//! println!("Accepted CommonCmd: {:?}", cmd); +//! Ok(()) +//! }, +//! cmd: BarCmd => { +//! cmd.set_output("Bar Result".to_string()); +//! println!("BarCmd's output: {}", cmd.output().unwrap()); +//! Ok(()) +//! }, +//! cmd: ComplexCmd => { +//! println!("Accepted ComplexCmd: {:?}", cmd); +//! Ok(()) +//! }, +//! _ => { +//! Err(errno!(EINVAL, "unknown command")) +//! } +//! }) +//! } +//! } +//! ``` + +/// A trait to unify all concrete types representing ioctl commands. +/// +/// The most useful property of this trait is that it supports downcasting +/// to the concrete type that actually implements the trait with the +/// `downcast_ref` and `downcast_mut` methods. +/// +/// ```rust +/// use async_io::ioctl::IoctlCmd; +/// +/// #[derive(Debug)] +/// pub struct DummyCmd; +/// impl IoctlCmd for DummyCmd {} +/// +/// let dummy : Box = Box::new(DummyCmd); +/// assert!(dummy.downcast_ref::().is_some()); +/// ``` +pub trait IoctlCmd: Downcast + Debug + Sync + Send {} +impl_downcast!(IoctlCmd); + +/// A convenient macro to define a struct for some type of ioctl command. +/// +/// Typcially, an ioctl command consists of an input argument and an output +/// output. As such, the struct defined by this macro has two fields: the +/// input and the output. +/// +/// The struct defined by this macro automatically implements the `IoctlCmd` +/// trait. +#[macro_export] +macro_rules! impl_ioctl_cmd { + ( + $(#[$outer:meta])* + pub struct $CmdName:ident {} + ) => { + $(#[$outer])* + pub struct $CmdName { + input: $Input, + output: Option<$Output>, + } + + #[allow(dead_code)] + impl $CmdName { + pub fn new(input: $Input) -> Self { + Self { + input, + output: None, + } + } + + pub fn input(&self) -> &$Input { + &self.input + } + + pub fn output(&self) -> Option<&$Output> { + self.output.as_ref() + } + + pub fn set_output(&mut self, output: $Output) { + self.output = Some(output) + } + + pub fn take_output(&mut self) -> Option<$Output> { + self.output.take() + } + } + + impl crate::fs::IoctlCmd for $CmdName {} + + impl std::fmt::Debug for $CmdName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(stringify!($CmdName)) + .field("input", self.input()) + .field("output", &self.output()) + .finish() + } + } + } +} + +use super::*; + +pub use self::get_ifconf::{GetIfConf, IfConf}; +pub use self::get_ifreq::{GetIfReq, GetIfReqWithRawCmd, IfReq}; +pub use self::get_readbuflen::GetReadBufLen; +pub use self::set_close_on_exec::*; +pub use self::set_nonblocking::SetNonBlocking; +pub use self::termios::*; +pub use self::winsize::*; +pub use net::socket::sockopt::SetSockOptRawCmd; + +mod get_ifconf; +mod get_ifreq; +mod get_readbuflen; +mod set_close_on_exec; +mod set_nonblocking; +mod termios; +mod winsize; + +use downcast_rs::{impl_downcast, Downcast}; +use std::fmt::Debug; + +#[macro_export] +macro_rules! match_ioctl_cmd_ref { + ( + $cmd:expr, + { + $( $bind:ident : $ty:ty => $arm:expr ),*, + _ => $default:expr + } + ) => {{ + let __cmd : &dyn crate::fs::IoctlCmd = $cmd; + $( + if __cmd.is::<$ty>() { + let $bind = __cmd.downcast_ref::<$ty>().unwrap(); + $arm + } else + )* + { + $default + } + }} +} + +#[macro_export] +macro_rules! match_ioctl_cmd_mut { + ( + $cmd:expr, + { + $( $bind:ident : $ty:ty => $arm:expr ),*, + _ => $default:expr + } + ) => {{ + let __cmd : &mut dyn crate::fs::IoctlCmd = $cmd; + $( + if __cmd.is::<$ty>() { + let $bind = __cmd.downcast_mut::<$ty>().unwrap(); + $arm + } else + )* + { + $default + } + }} +} + +// Macro for ioctl auto error number. +// If the corresponding cmds are not defined, a default error number will be return +#[macro_export] +macro_rules! match_ioctl_cmd_auto_error { + ( + $cmd:expr, + { + $( $bind:ident : $ty:ty => $arm:expr ),* $(,)? + } + ) => {{ + use crate::fs::*; + let __cmd : &mut dyn crate::fs::IoctlCmd = $cmd; + $( + if __cmd.is::<$ty>() { + let $bind = __cmd.downcast_mut::<$ty>().unwrap(); + $arm + } else + )* + // If the corresponding cmds are not defined, it will go here for default error + if __cmd.is::() { + return_errno!(Errno::ENOTTY, "not tty device"); + } + else if __cmd.is::() { + return_errno!(Errno::ENOTTY, "not tty device"); + } + else if __cmd.is::() { + return_errno!(Errno::ENOTTY, "not tty device"); + } + else if __cmd.is::() { + return_errno!(Errno::ENOTTY, "not tty device"); + } + else { + // Default branch + return_errno!(EINVAL, "unsupported ioctl cmd"); + } + }} +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/set_close_on_exec.rs b/src/libos/src/fs/file_ops/ioctl/builtin/set_close_on_exec.rs new file mode 100644 index 00000000..34283f14 --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/set_close_on_exec.rs @@ -0,0 +1,5 @@ +use super::*; + +impl_ioctl_cmd! { + pub struct SetCloseOnExec {} +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/set_nonblocking.rs b/src/libos/src/fs/file_ops/ioctl/builtin/set_nonblocking.rs new file mode 100644 index 00000000..35307bb5 --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/set_nonblocking.rs @@ -0,0 +1,24 @@ +use super::*; + +impl_ioctl_cmd! { + pub struct SetNonBlocking {} +} + +impl SetNonBlocking { + pub fn execute(&mut self, host_fd: FileDesc) -> Result<()> { + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::FIONBIO as i32, + self.input() as *const i32 as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + + Ok(()) + } +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin.rs b/src/libos/src/fs/file_ops/ioctl/builtin/termios.rs similarity index 64% rename from src/libos/src/fs/file_ops/ioctl/builtin.rs rename to src/libos/src/fs/file_ops/ioctl/builtin/termios.rs index bc608b1c..ee842051 100644 --- a/src/libos/src/fs/file_ops/ioctl/builtin.rs +++ b/src/libos/src/fs/file_ops/ioctl/builtin/termios.rs @@ -1,31 +1,5 @@ -//! Built-in ioctls. - use super::*; -#[derive(Debug)] -#[repr(C)] -pub struct WinSize { - pub ws_row: u16, - pub ws_col: u16, - pub ws_xpixel: u16, - pub ws_ypixel: u16, -} - -#[derive(Debug)] -#[repr(C)] -pub struct IfConf { - pub ifc_len: i32, - pub ifc_buf: *const u8, -} - -const IFNAMSIZ: usize = 16; -#[derive(Debug)] -#[repr(C)] -pub struct IfReq { - pub ifr_name: [u8; IFNAMSIZ], - pub ifr_union: [u8; 24], -} - /* The termios structure used in the Linux kernel is not the same as we use in the glibc. Thus, we have two definitions here. @@ -78,49 +52,10 @@ impl KernelTermios { c_ospeed: 0, } } - - pub fn execute_tcgets(&mut self, host_fd: i32, cmd_num: i32) -> Result { - debug_assert!(cmd_num == 0x5401); - let mut termios = self.to_termios(); - let len = std::mem::size_of::(); - let ret = try_libc!({ - let mut retval: i32 = 0; - let status = occlum_ocall_ioctl( - &mut retval as *mut i32, - host_fd, - cmd_num, - &mut termios as *const Termios as *mut c_void, - len, - ); - assert!(status == sgx_status_t::SGX_SUCCESS); - retval - }); - *self = termios.to_kernel_termios(); - Ok(ret) - } - - pub fn execute_tcsets(&self, host_fd: i32, cmd_num: i32) -> Result { - debug_assert!(cmd_num == 0x5402); - let termios = self.to_termios(); - let len = std::mem::size_of::(); - let ret = try_libc!({ - let mut retval: i32 = 0; - let status = occlum_ocall_ioctl( - &mut retval as *mut i32, - host_fd, - cmd_num, - &termios as *const Termios as *mut c_void, - len, - ); - assert!(status == sgx_status_t::SGX_SUCCESS); - retval - }); - Ok(ret) - } } impl Termios { - pub fn to_kernel_termios(&self) -> KernelTermios { + fn to_kernel_termios(&self) -> KernelTermios { let mut c_cc = [0; KERNEL_NCCS]; c_cc.copy_from_slice(&self.c_cc[..KERNEL_NCCS]); @@ -134,3 +69,55 @@ impl Termios { } } } + +impl_ioctl_cmd! { + pub struct TcGets {} +} + +impl TcGets { + pub fn execute(&mut self, host_fd: FileDesc) -> Result<()> { + let mut termios: Termios = Default::default(); + + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::TCGETS as i32, + &mut termios as *mut Termios as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + + let kernel_termios = termios.to_kernel_termios(); + trace!("kernel termios = {:?}", kernel_termios); + self.set_output(kernel_termios); + Ok(()) + } +} + +impl_ioctl_cmd! { + pub struct TcSets {} +} + +impl TcSets { + pub fn execute(&self, host_fd: FileDesc) -> Result<()> { + let kernel_termios = self.input(); + let termios = kernel_termios.to_termios(); + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::TCSETS as i32, + &termios as *const Termios as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + Ok(()) + } +} diff --git a/src/libos/src/fs/file_ops/ioctl/builtin/winsize.rs b/src/libos/src/fs/file_ops/ioctl/builtin/winsize.rs new file mode 100644 index 00000000..d00be03e --- /dev/null +++ b/src/libos/src/fs/file_ops/ioctl/builtin/winsize.rs @@ -0,0 +1,64 @@ +use super::*; + +#[derive(Default, Clone, Copy, Debug)] +#[repr(C)] +pub struct WinSize { + pub ws_row: u16, + pub ws_col: u16, + pub ws_xpixel: u16, + pub ws_ypixel: u16, +} + +impl_ioctl_cmd! { + pub struct SetWinSize {} +} + +impl SetWinSize { + pub fn execute(&self, host_fd: FileDesc) -> Result<()> { + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::TIOCSWINSZ as i32, + self.input() as *const WinSize as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + Ok(()) + } +} + +impl_ioctl_cmd! { + pub struct GetWinSize {} +} + +impl GetWinSize { + pub fn execute(&mut self, host_fd: FileDesc) -> Result<()> { + let mut winsize: WinSize = Default::default(); + + try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + BuiltinIoctlNum::TIOCGWINSZ as i32, + &mut winsize as *mut WinSize as *mut c_void, + std::mem::size_of::(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + + if winsize.ws_row == 0 || winsize.ws_col == 0 { + warn!( + "window size: row: {:?}, col: {:?}", + winsize.ws_row, winsize.ws_col + ); + } + self.set_output(winsize); + Ok(()) + } +} diff --git a/src/libos/src/fs/file_ops/ioctl/macros.rs b/src/libos/src/fs/file_ops/ioctl/macros.rs index f8caf909..258a7ff8 100644 --- a/src/libos/src/fs/file_ops/ioctl/macros.rs +++ b/src/libos/src/fs/file_ops/ioctl/macros.rs @@ -1,7 +1,7 @@ -//! Macros to implement `BuiltinIoctlNum` and `IoctlCmd` given a list of ioctl +//! Macros to implement `BuiltinIoctlNum` and `IoctlRawCmd` given a list of ioctl //! names, numbers, and argument types. -/// Implement `BuiltinIoctlNum` and `IoctlCmd`. +/// Implement `BuiltinIoctlNum` and `IoctlRawCmd`. #[macro_export] macro_rules! impl_ioctl_nums_and_cmds { ($( $ioctl_name: ident => ( $ioctl_num: expr, $($ioctl_type_tt: tt)* ) ),+,) => { @@ -12,7 +12,7 @@ macro_rules! impl_ioctl_nums_and_cmds { )* } - // Implement IoctlCmd given ioctl names and their argument types + // Implement IoctlRawCmd given ioctl names and their argument types impl_ioctl_cmds! { $( $ioctl_name => ( $($ioctl_type_tt)*), @@ -57,21 +57,21 @@ macro_rules! impl_builtin_ioctl_nums { } //////////////////////////////////////////////////////////////////////////////// -// IoctlCmd +// IoctlRawCmd //////////////////////////////////////////////////////////////////////////////// macro_rules! impl_ioctl_cmds { ($( $ioctl_name: ident => ( $($ioctl_type_tt: tt)* ) ),+,) => { #[derive(Debug)] - pub enum IoctlCmd<'a> { + pub enum IoctlRawCmd<'a> { $( $ioctl_name( get_arg_type!($($ioctl_type_tt)*) ), )* NonBuiltin(NonBuiltinIoctlCmd<'a>), } - impl<'a> IoctlCmd<'a> { - pub unsafe fn new(cmd_num: u32, arg_ptr: *mut u8) -> Result> { + impl<'a> IoctlRawCmd<'a> { + pub unsafe fn new(cmd_num: u32, arg_ptr: *mut u8) -> Result> { if let Some(builtin_cmd_num) = BuiltinIoctlNum::from_u32(cmd_num) { unsafe { Self::new_builtin_cmd(builtin_cmd_num, arg_ptr) } } else { @@ -79,7 +79,7 @@ macro_rules! impl_ioctl_cmds { } } - unsafe fn new_builtin_cmd(cmd_num: BuiltinIoctlNum, arg_ptr: *mut u8) -> Result> { + unsafe fn new_builtin_cmd(cmd_num: BuiltinIoctlNum, arg_ptr: *mut u8) -> Result> { if cmd_num.require_arg() && arg_ptr.is_null() { return_errno!(EINVAL, "arg_ptr cannot be null"); } @@ -90,43 +90,43 @@ macro_rules! impl_ioctl_cmds { $( BuiltinIoctlNum::$ioctl_name => { let arg = get_arg!($($ioctl_type_tt)*, arg_ptr); - IoctlCmd::$ioctl_name(arg) + IoctlRawCmd::$ioctl_name(arg) } )* }; Ok(cmd) } - unsafe fn new_nonbuiltin_cmd(cmd_num: u32, arg_ptr: *mut u8) -> Result> { + unsafe fn new_nonbuiltin_cmd(cmd_num: u32, arg_ptr: *mut u8) -> Result> { let structured_cmd_num = StructuredIoctlNum::from_u32(cmd_num)?; let inner_cmd = unsafe { NonBuiltinIoctlCmd::new(structured_cmd_num, arg_ptr)? }; - Ok(IoctlCmd::NonBuiltin(inner_cmd)) + Ok(IoctlRawCmd::NonBuiltin(inner_cmd)) } pub fn arg_ptr(&self) -> *const u8 { match self { $( - IoctlCmd::$ioctl_name(arg_ref) => get_arg_ptr!($($ioctl_type_tt)*, arg_ref), + IoctlRawCmd::$ioctl_name(arg_ref) => get_arg_ptr!($($ioctl_type_tt)*, arg_ref), )* - IoctlCmd::NonBuiltin(inner) => inner.arg_ptr(), + IoctlRawCmd::NonBuiltin(inner) => inner.arg_ptr(), } } pub fn arg_len(&self) -> usize { match self { $( - IoctlCmd::$ioctl_name(_) => get_arg_len!($($ioctl_type_tt)*), + IoctlRawCmd::$ioctl_name(_) => get_arg_len!($($ioctl_type_tt)*), )* - IoctlCmd::NonBuiltin(inner) => inner.arg_len(), + IoctlRawCmd::NonBuiltin(inner) => inner.arg_len(), } } pub fn cmd_num(&self) -> u32 { match self { $( - IoctlCmd::$ioctl_name(_) => BuiltinIoctlNum::$ioctl_name as u32, + IoctlRawCmd::$ioctl_name(_) => BuiltinIoctlNum::$ioctl_name as u32, )* - IoctlCmd::NonBuiltin(inner) => inner.cmd_num().as_u32(), + IoctlRawCmd::NonBuiltin(inner) => inner.cmd_num().as_u32(), } } } diff --git a/src/libos/src/fs/file_ops/ioctl/mod.rs b/src/libos/src/fs/file_ops/ioctl/mod.rs index 579d2169..cfafa244 100644 --- a/src/libos/src/fs/file_ops/ioctl/mod.rs +++ b/src/libos/src/fs/file_ops/ioctl/mod.rs @@ -6,8 +6,13 @@ use super::*; -pub use self::builtin::*; +use self::builtin::*; +pub use self::builtin::{ + GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, GetWinSize, IfConf, IoctlCmd, SetNonBlocking, + SetWinSize, TcGets, TcSets, +}; pub use self::non_builtin::{NonBuiltinIoctlCmd, StructuredIoctlArgType, StructuredIoctlNum}; +use crate::util::mem_util::from_user; #[macro_use] mod macros; @@ -18,7 +23,7 @@ mod non_builtin; /// /// By giving the names, numbers, and argument types of built-in ioctls, /// the macro below generates the corresponding code of `BuiltinIoctlNum` and -/// `IoctlCmd`. +/// `IoctlRawCmd`. /// /// To add a new built-in ioctl, just follow the convention as shown /// by existing built-in ioctls. @@ -68,51 +73,121 @@ impl_ioctl_nums_and_cmds! { /// /// Sanity checks are mostly useful when the argument values are returned from /// the untrusted host OS. -impl<'a> IoctlCmd<'a> { - pub fn validate_arg_and_ret_vals(&self, ret: i32) -> Result<()> { +impl<'a> IoctlRawCmd<'a> { + pub fn to_safe_ioctlcmd(&self) -> Result> { + Ok(match self { + IoctlRawCmd::TCGETS(_) => Box::new(TcGets::new(())), + IoctlRawCmd::TCSETS(termios_ref) => { + let termios = **termios_ref; + Box::new(TcSets::new(termios)) + } + IoctlRawCmd::TIOCGWINSZ(_) => Box::new(GetWinSize::new(())), + IoctlRawCmd::TIOCSWINSZ(winsize_ref) => { + let winsize = **winsize_ref; + Box::new(SetWinSize::new(winsize)) + } + IoctlRawCmd::NonBuiltin(inner) => { + let nonbuiltin_cmd = + unsafe { NonBuiltinIoctlCmd::new(*inner.cmd_num(), inner.arg_ptr() as _)? }; + Box::new(nonbuiltin_cmd) + } + IoctlRawCmd::FIONBIO(non_blocking) => Box::new(SetNonBlocking::new(**non_blocking)), + IoctlRawCmd::FIONREAD(_) => Box::new(GetReadBufLen::new(())), + IoctlRawCmd::FIONCLEX(_) => Box::new(SetCloseOnExec::new(false)), + IoctlRawCmd::FIOCLEX(_) => Box::new(SetCloseOnExec::new(true)), + IoctlRawCmd::SIOCGIFCONF(ifconf_mut) => { + if !ifconf_mut.ifc_buf.is_null() { + if ifconf_mut.ifc_len < 0 { + return_errno!(EINVAL, "invalid ifc_len"); + } + from_user::check_array(ifconf_mut.ifc_buf, ifconf_mut.ifc_len as usize)?; + } + Box::new(GetIfConf::new(ifconf_mut)) + } + IoctlRawCmd::SIOCGIFFLAGS(req) + | IoctlRawCmd::SIOCGIFNAME(req) + | IoctlRawCmd::SIOCGIFADDR(req) + | IoctlRawCmd::SIOCGIFDSTADDR(req) + | IoctlRawCmd::SIOCGIFBRDADDR(req) + | IoctlRawCmd::SIOCGIFNETMASK(req) + | IoctlRawCmd::SIOCGIFMTU(req) + | IoctlRawCmd::SIOCGIFHWADDR(req) + | IoctlRawCmd::SIOCGIFINDEX(req) + | IoctlRawCmd::SIOCGIFPFLAGS(req) + | IoctlRawCmd::SIOCGIFTXQLEN(req) + | IoctlRawCmd::SIOCGIFMAP(req) => { + Box::new(GetIfReqWithRawCmd::new(self.cmd_num(), **req)) + } + _ => { + return_errno!(EINVAL, "unsupported cmd"); + } + }) + } + + pub fn copy_output_from_safe(&mut self, cmd: &dyn IoctlCmd) { match self { - IoctlCmd::TIOCGWINSZ(winsize_ref) => { - // ws_row and ws_col are usually not zeros - if winsize_ref.ws_row == 0 || winsize_ref.ws_col == 0 { - warn!( - "window size: row: {:?}, col: {:?}", - winsize_ref.ws_row, winsize_ref.ws_col - ); + IoctlRawCmd::TCGETS(termios_mut) => { + let cmd = cmd.downcast_ref::().unwrap(); + **termios_mut = *cmd.output().unwrap(); + } + IoctlRawCmd::TIOCGWINSZ(winsize_mut) => { + let cmd = cmd.downcast_ref::().unwrap(); + **winsize_mut = *cmd.output().unwrap(); + } + IoctlRawCmd::FIONREAD(len_mut) => { + let cmd = cmd.downcast_ref::().unwrap(); + **len_mut = *cmd.output().unwrap(); + } + IoctlRawCmd::SIOCGIFCONF(ifconf_mut) => { + let cmd = cmd.downcast_ref::().unwrap(); + ifconf_mut.ifc_len = cmd.len() as i32; + if !ifconf_mut.ifc_buf.is_null() { + let mut raw_buf = unsafe { + std::slice::from_raw_parts_mut( + ifconf_mut.ifc_buf as _, + ifconf_mut.ifc_len as _, + ) + }; + raw_buf.copy_from_slice(cmd.as_slice().unwrap()); } } - IoctlCmd::FIONREAD(nread_ref) => { - if (**nread_ref < 0) { - return_errno!(EINVAL, "invalid data from host"); - } + IoctlRawCmd::SIOCGIFNAME(ifreq_mut) + | IoctlRawCmd::SIOCGIFFLAGS(ifreq_mut) + | IoctlRawCmd::SIOCGIFADDR(ifreq_mut) + | IoctlRawCmd::SIOCGIFDSTADDR(ifreq_mut) + | IoctlRawCmd::SIOCGIFBRDADDR(ifreq_mut) + | IoctlRawCmd::SIOCGIFNETMASK(ifreq_mut) + | IoctlRawCmd::SIOCGIFMTU(ifreq_mut) + | IoctlRawCmd::SIOCGIFHWADDR(ifreq_mut) + | IoctlRawCmd::SIOCGIFINDEX(ifreq_mut) + | IoctlRawCmd::SIOCGIFPFLAGS(ifreq_mut) + | IoctlRawCmd::SIOCGIFTXQLEN(ifreq_mut) + | IoctlRawCmd::SIOCGIFMAP(ifreq_mut) => { + let cmd = cmd.downcast_ref::().unwrap(); + **ifreq_mut = *cmd.output().unwrap(); } _ => {} } - - // Current ioctl commands all return zero - if ret != 0 { - return_errno!(EINVAL, "return value should be zero"); - } - Ok(()) } } -pub fn do_ioctl(fd: FileDesc, cmd: &mut IoctlCmd) -> Result { - debug!("ioctl: fd: {}, cmd: {:?}", fd, cmd); +pub fn do_ioctl(fd: FileDesc, raw_cmd: &mut IoctlRawCmd<'_>) -> Result { + debug!("ioctl: fd: {}, cmd: {:?}", fd, raw_cmd); let current = current!(); let file_ref = current.file(fd)?; - let mut file_table = current.files().lock().unwrap(); - let mut entry = file_table.get_entry_mut(fd)?; - match cmd { - IoctlCmd::FIONCLEX(_) => { - entry.set_close_on_spawn(false); - return Ok(0); - } - IoctlCmd::FIOCLEX(_) => { - entry.set_close_on_spawn(true); - return Ok(0); - } - _ => return file_ref.ioctl(cmd), + let mut cmd = raw_cmd.to_safe_ioctlcmd()?; + + if cmd.is::() { + let is_close_on_exec = cmd.downcast_ref::().unwrap().input(); + let mut file_table = current.files().lock(); + let entry = file_table.get_entry_mut(fd)?; + entry.set_close_on_spawn(*is_close_on_exec); + return Ok(0); } + + file_ref.ioctl(cmd.as_mut())?; + raw_cmd.copy_output_from_safe(cmd.as_ref()); + Ok(0) } extern "C" { diff --git a/src/libos/src/fs/file_ops/ioctl/non_builtin.rs b/src/libos/src/fs/file_ops/ioctl/non_builtin.rs index 542349cd..93ce3066 100644 --- a/src/libos/src/fs/file_ops/ioctl/non_builtin.rs +++ b/src/libos/src/fs/file_ops/ioctl/non_builtin.rs @@ -69,8 +69,30 @@ impl<'a> NonBuiltinIoctlCmd<'a> { pub fn arg_len(&self) -> usize { self.cmd_num.arg_size() } + + pub fn execute(&self, host_fd: FileDesc) -> Result<()> { + let cmd_num = self.cmd_num().as_u32() as c_int; + let cmd_arg_ptr = self.arg_ptr() as *mut c_void; + + let ret = try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + host_fd as i32, + cmd_num, + cmd_arg_ptr, + self.arg_len(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + + Ok(()) + } } +impl IoctlCmd for NonBuiltinIoctlCmd<'static> {} + #[derive(Debug, Copy, Clone, PartialEq)] pub struct StructuredIoctlNum { cmd_id: u8, diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index 101b7a95..4f34f94d 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -14,8 +14,9 @@ pub use self::fspath::{get_abs_path_by_fd, FsPath, AT_FDCWD}; pub use self::fsync::{do_fdatasync, do_fsync}; pub use self::getdents::{do_getdents, do_getdents64}; pub use self::ioctl::{ - do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd, StructuredIoctlArgType, - StructuredIoctlNum, + do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, + GetWinSize, IfConf, IoctlCmd, IoctlRawCmd, NonBuiltinIoctlCmd, SetNonBlocking, SetWinSize, + StructuredIoctlArgType, StructuredIoctlNum, TcGets, TcSets, }; pub use self::link::{do_linkat, LinkFlags}; pub use self::lseek::do_lseek; diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 13c3256d..165cddad 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -1,4 +1,5 @@ use super::*; +use crate::fs::IoctlCmd; use crate::net::PollEventFlags; use crate::process::do_getuid; use rcore_fs::vfs::FallocateMode; @@ -275,16 +276,13 @@ impl File for INodeFile { self.unlock_range_lock(&range_lock) } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - match cmd { - IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), - IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), - _ => {} - }; - let cmd_num = cmd.cmd_num(); - let cmd_argp = cmd.arg_ptr() as usize; - self.inode.io_control(cmd_num, cmd_argp)?; - Ok(0) + fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { + match_ioctl_cmd_auto_error!(cmd, { + cmd : NonBuiltinIoctlCmd => { + self.inode.io_control(cmd.cmd_num().as_u32(), cmd.arg_ptr() as usize)?; + }, + }); + Ok(()) } fn poll_new(&self) -> IoEvents { diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 008807f2..7b7b43b4 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -19,8 +19,9 @@ pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier}; pub use self::file::{File, FileRef}; pub use self::file_ops::{ occlum_ocall_ioctl, utimbuf_t, AccessMode, BuiltinIoctlNum, CreationFlags, FallocateFlags, - FileMode, IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, - STATUS_FLAGS_MASK, + FileMode, GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, GetWinSize, IfConf, IoctlCmd, + IoctlRawCmd, NonBuiltinIoctlCmd, SetNonBlocking, SetWinSize, Stat, StatusFlags, + StructuredIoctlArgType, StructuredIoctlNum, TcGets, TcSets, STATUS_FLAGS_MASK, }; pub use self::file_table::{FileDesc, FileTable, FileTableEvent, FileTableNotifier}; pub use self::fs_ops::Statfs; diff --git a/src/libos/src/fs/pipe.rs b/src/libos/src/fs/pipe.rs index 4e6e312f..6e768a36 100644 --- a/src/libos/src/fs/pipe.rs +++ b/src/libos/src/fs/pipe.rs @@ -1,8 +1,10 @@ use atomic::{Atomic, Ordering}; +use net::PollEventFlags; use super::channel::{Channel, Consumer, Producer}; use super::*; -use net::PollEventFlags; + +use crate::fs::{GetReadBufLen, IoctlCmd}; // TODO: Add F_SETPIPE_SZ in fcntl to dynamically change the size of pipe // to improve memory efficiency. This value is got from /proc/sys/fs/pipe-max-size on linux. @@ -107,18 +109,14 @@ impl File for PipeReader { self } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - match cmd { - IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), - IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), - IoctlCmd::FIONREAD(arg) => { - let ready_len = self.get_ready_len().min(std::i32::MAX as usize) as i32; - **arg = ready_len; - return Ok(0); - } - _ => return_errno!(ENOSYS, "not supported"), - }; - unreachable!(); + fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { + match_ioctl_cmd_auto_error!(cmd, { + cmd : GetReadBufLen => { + let read_buf_len = self.consumer.ready_len(); + cmd.set_output(read_buf_len as _); + }, + }); + Ok(()) } } @@ -209,13 +207,9 @@ impl File for PipeWriter { self } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - match cmd { - IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), - IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), - _ => return_errno!(ENOSYS, "not supported"), - }; - unreachable!(); + fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { + match_ioctl_cmd_auto_error!(cmd, {}); + Ok(()) } } diff --git a/src/libos/src/fs/stdio.rs b/src/libos/src/fs/stdio.rs index f11f6bc5..5d27a24b 100644 --- a/src/libos/src/fs/stdio.rs +++ b/src/libos/src/fs/stdio.rs @@ -4,6 +4,9 @@ use core::cmp; use std::io::{BufReader, LineWriter}; use std::sync::SgxMutex; +use crate::fs::file_ops::{GetWinSize, SetWinSize, TcGets, TcSets}; +use crate::fs::IoctlCmd; + macro_rules! try_libc_stdio { ($ret: expr) => {{ let ret = unsafe { $ret }; @@ -169,50 +172,6 @@ impl File for StdoutFile { Ok(()) } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - let host_stdout_fd = self.host_fd() as i32; - let cmd_bits = cmd.cmd_num() as c_int; - - // Handle special case for TCGETS/TCSETS which use different structures - // in linux kernel and libc - match cmd { - IoctlCmd::TCGETS(kernel_termios) => { - return kernel_termios.execute_tcgets(host_stdout_fd, cmd_bits); - } - IoctlCmd::TCSETS(kernel_termios) => { - return kernel_termios.execute_tcsets(host_stdout_fd, cmd_bits); - } - _ => {} - }; - - let can_delegate_to_host = match cmd { - IoctlCmd::TIOCGWINSZ(_) => true, - IoctlCmd::TIOCSWINSZ(_) => true, - _ => false, - }; - if !can_delegate_to_host { - return_errno!(EINVAL, "unknown ioctl cmd for stdout"); - } - - let cmd_arg_ptr = cmd.arg_ptr() as *mut c_void; - let cmd_arg_len = cmd.arg_len(); - let ret = try_libc!({ - let mut retval: i32 = 0; - let status = occlum_ocall_ioctl( - &mut retval as *mut i32, - host_stdout_fd, - cmd_bits, - cmd_arg_ptr, - cmd_arg_len, - ); - assert!(status == sgx_status_t::SGX_SUCCESS); - retval - }); - cmd.validate_arg_and_ret_vals(ret)?; - - Ok(ret) - } - fn status_flags(&self) -> Result { let ret = try_libc!(libc::ocall::fcntl_arg0( self.host_fd() as i32, @@ -359,48 +318,8 @@ impl File for StdinFile { }) } - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - let host_stdin_fd = self.host_fd() as i32; - let cmd_bits = cmd.cmd_num() as c_int; - - // Handle special case for TCGETS/TCSETS which use different structures - // in linux kernel and libc - match cmd { - IoctlCmd::TCGETS(kernel_termios) => { - return kernel_termios.execute_tcgets(host_stdin_fd, cmd_bits); - } - IoctlCmd::TCSETS(kernel_termios) => { - return kernel_termios.execute_tcsets(host_stdin_fd, cmd_bits); - } - _ => {} - }; - - let can_delegate_to_host = match cmd { - IoctlCmd::TIOCGWINSZ(_) => true, - IoctlCmd::TIOCSWINSZ(_) => true, - _ => false, - }; - if !can_delegate_to_host { - return_errno!(EINVAL, "unknown ioctl cmd for stdin"); - } - - let cmd_arg_ptr = cmd.arg_ptr() as *mut c_void; - let cmd_arg_len = cmd.arg_len(); - let ret = try_libc!({ - let mut retval: i32 = 0; - let status = occlum_ocall_ioctl( - &mut retval as *mut i32, - host_stdin_fd, - cmd_bits, - cmd_arg_ptr, - cmd_arg_len, - ); - assert!(status == sgx_status_t::SGX_SUCCESS); - retval - }); - cmd.validate_arg_and_ret_vals(ret)?; - - Ok(ret) + fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> { + stdio_ioctl(cmd, self.host_fd()) } fn status_flags(&self) -> Result { @@ -444,3 +363,22 @@ impl Debug for StdinFile { unsafe impl Send for StdinFile {} unsafe impl Sync for StdinFile {} + +fn stdio_ioctl(cmd: &mut dyn IoctlCmd, host_fd: FileDesc) -> Result<()> { + debug!("stdio ioctl: cmd: {:?}", cmd); + match_ioctl_cmd_auto_error!(cmd, { + cmd : TcGets => { + cmd.execute(host_fd)? + }, + cmd : TcSets => { + cmd.execute(host_fd)? + }, + cmd : SetWinSize => { + cmd.execute(host_fd)? + }, + cmd : GetWinSize => { + cmd.execute(host_fd)? + }, + }); + Ok(()) +} diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index f693953a..23cb49b3 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -673,7 +673,7 @@ pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result { if argp.is_null() == false { from_user::check_mut_ptr(argp)?; } - IoctlCmd::new(cmd, argp)? + IoctlRawCmd::new(cmd, argp)? }; file_ops::do_ioctl(fd, &mut ioctl_cmd)?; Ok(0) diff --git a/src/libos/src/fs/timer_file.rs b/src/libos/src/fs/timer_file.rs index a5042b14..8893f6ca 100644 --- a/src/libos/src/fs/timer_file.rs +++ b/src/libos/src/fs/timer_file.rs @@ -115,11 +115,6 @@ impl File for TimerFile { Ok(ret) } - // TODO: implement ioctl - // fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - // self.ioctl_impl(cmd) - // } - fn access_mode(&self) -> Result { Ok(AccessMode::O_RDWR) }