[libos] Re-implement ioctl commands

This commit is contained in:
ClawSeven 2024-04-30 11:41:44 +08:00 committed by volcano
parent fd6e1bae45
commit f3b1acf3ce
20 changed files with 906 additions and 250 deletions

@ -45,7 +45,7 @@ impl INode for DevSgx {
fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> { fn io_control(&self, _cmd: u32, _data: usize) -> vfs::Result<()> {
let mut ioctl_cmd = 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| { self.ioctl(&mut ioctl_cmd).map_err(|e| {
error!("{}", e.backtrace()); error!("{}", e.backtrace());
FsError::IOCTLError FsError::IOCTLError
@ -59,9 +59,9 @@ impl INode for DevSgx {
} }
impl DevSgx { impl DevSgx {
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut IoctlRawCmd) -> Result<i32> {
let nonbuiltin_cmd = match cmd { 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"), _ => return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"),
}; };
let cmd_num = nonbuiltin_cmd.cmd_num().as_u32(); let cmd_num = nonbuiltin_cmd.cmd_num().as_u32();

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::fs::IoctlCmd;
macro_rules! return_op_unsupported_error { macro_rules! return_op_unsupported_error {
($op_name: expr, $errno: expr) => {{ ($op_name: expr, $errno: expr) => {{
@ -79,8 +80,8 @@ pub trait File: Debug + Sync + Send + Any {
Ok(()) Ok(())
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, _cmd: &mut dyn IoctlCmd) -> Result<()> {
return_op_unsupported_error!("ioctl") return_op_unsupported_error!("ioctl", EINVAL);
} }
fn access_mode(&self) -> Result<AccessMode> { fn access_mode(&self) -> Result<AccessMode> {
@ -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. /// Return the host fd, if the file is backed by an underlying host file.
fn host_fd(&self) -> Option<&HostFd> { fn host_fd(&self) -> Option<&HostFd> {
return None; None
} }
/// Update the ready events of a host file. /// Update the ready events of a host file.

@ -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<u8>),
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(())
}

@ -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<IfReq> {
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::<IfReq>(),
);
assert!(status == sgx_types::sgx_status_t::SGX_SUCCESS);
retval
});
Ok(if_req)
}
impl IoctlCmd for GetIfReqWithRawCmd {}
impl_ioctl_cmd! {
pub struct GetIfReq<Input=IfReq, Output=IfReq> {}
}

@ -0,0 +1,28 @@
use super::*;
impl_ioctl_cmd! {
pub struct GetReadBufLen<Input=(), Output=i32> {}
}
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::<i32>(),
);
assert!(status == sgx_status_t::SGX_SUCCESS);
retval
});
trace!("read buf len = {:?}", buflen);
self.set_output(buflen);
Ok(())
}
}

@ -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<Input=(), Output=bool> {}
//! }
//!
//! impl_ioctl_cmd! {
//! pub struct FooCmd<Input=i32, Output=()> {}
//! }
//!
//! impl_ioctl_cmd! {
//! pub struct BarCmd<Input=i32, Output=String> {}
//! }
//!
//! // 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<dyn IoctlCmd> = Box::new(DummyCmd);
/// assert!(dummy.downcast_ref::<DummyCmd>().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 <Input=$Input:ty, Output=$Output:ty> {}
) => {
$(#[$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::<TcGets>() {
return_errno!(Errno::ENOTTY, "not tty device");
}
else if __cmd.is::<TcSets>() {
return_errno!(Errno::ENOTTY, "not tty device");
}
else if __cmd.is::<GetWinSize>() {
return_errno!(Errno::ENOTTY, "not tty device");
}
else if __cmd.is::<SetWinSize>() {
return_errno!(Errno::ENOTTY, "not tty device");
}
else {
// Default branch
return_errno!(EINVAL, "unsupported ioctl cmd");
}
}}
}

@ -0,0 +1,5 @@
use super::*;
impl_ioctl_cmd! {
pub struct SetCloseOnExec<Input=bool, Output=()> {}
}

@ -0,0 +1,24 @@
use super::*;
impl_ioctl_cmd! {
pub struct SetNonBlocking<Input=i32, Output=()> {}
}
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::<i32>(),
);
assert!(status == sgx_status_t::SGX_SUCCESS);
retval
});
Ok(())
}
}

@ -1,31 +1,5 @@
//! Built-in ioctls.
use super::*; 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 The termios structure used in the Linux kernel is not the same as we use in the glibc. Thus, we have two
definitions here. definitions here.
@ -78,49 +52,10 @@ impl KernelTermios {
c_ospeed: 0, c_ospeed: 0,
} }
} }
pub fn execute_tcgets(&mut self, host_fd: i32, cmd_num: i32) -> Result<i32> {
debug_assert!(cmd_num == 0x5401);
let mut termios = self.to_termios();
let len = std::mem::size_of::<Termios>();
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<i32> {
debug_assert!(cmd_num == 0x5402);
let termios = self.to_termios();
let len = std::mem::size_of::<Termios>();
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 { impl Termios {
pub fn to_kernel_termios(&self) -> KernelTermios { fn to_kernel_termios(&self) -> KernelTermios {
let mut c_cc = [0; KERNEL_NCCS]; let mut c_cc = [0; KERNEL_NCCS];
c_cc.copy_from_slice(&self.c_cc[..KERNEL_NCCS]); c_cc.copy_from_slice(&self.c_cc[..KERNEL_NCCS]);
@ -134,3 +69,55 @@ impl Termios {
} }
} }
} }
impl_ioctl_cmd! {
pub struct TcGets<Input=(), Output=KernelTermios> {}
}
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::<Termios>(),
);
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<Input=KernelTermios, Output=()> {}
}
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::<Termios>(),
);
assert!(status == sgx_status_t::SGX_SUCCESS);
retval
});
Ok(())
}
}

@ -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<Input=WinSize, Output=()> {}
}
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::<WinSize>(),
);
assert!(status == sgx_status_t::SGX_SUCCESS);
retval
});
Ok(())
}
}
impl_ioctl_cmd! {
pub struct GetWinSize<Input=(), Output=WinSize> {}
}
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::<WinSize>(),
);
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(())
}
}

@ -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. //! names, numbers, and argument types.
/// Implement `BuiltinIoctlNum` and `IoctlCmd`. /// Implement `BuiltinIoctlNum` and `IoctlRawCmd`.
#[macro_export] #[macro_export]
macro_rules! impl_ioctl_nums_and_cmds { macro_rules! impl_ioctl_nums_and_cmds {
($( $ioctl_name: ident => ( $ioctl_num: expr, $($ioctl_type_tt: tt)* ) ),+,) => { ($( $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! { impl_ioctl_cmds! {
$( $(
$ioctl_name => ( $($ioctl_type_tt)*), $ioctl_name => ( $($ioctl_type_tt)*),
@ -57,21 +57,21 @@ macro_rules! impl_builtin_ioctl_nums {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// IoctlCmd // IoctlRawCmd
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
macro_rules! impl_ioctl_cmds { macro_rules! impl_ioctl_cmds {
($( $ioctl_name: ident => ( $($ioctl_type_tt: tt)* ) ),+,) => { ($( $ioctl_name: ident => ( $($ioctl_type_tt: tt)* ) ),+,) => {
#[derive(Debug)] #[derive(Debug)]
pub enum IoctlCmd<'a> { pub enum IoctlRawCmd<'a> {
$( $(
$ioctl_name( get_arg_type!($($ioctl_type_tt)*) ), $ioctl_name( get_arg_type!($($ioctl_type_tt)*) ),
)* )*
NonBuiltin(NonBuiltinIoctlCmd<'a>), NonBuiltin(NonBuiltinIoctlCmd<'a>),
} }
impl<'a> IoctlCmd<'a> { impl<'a> IoctlRawCmd<'a> {
pub unsafe fn new(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> { pub unsafe fn new(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlRawCmd<'a>> {
if let Some(builtin_cmd_num) = BuiltinIoctlNum::from_u32(cmd_num) { if let Some(builtin_cmd_num) = BuiltinIoctlNum::from_u32(cmd_num) {
unsafe { Self::new_builtin_cmd(builtin_cmd_num, arg_ptr) } unsafe { Self::new_builtin_cmd(builtin_cmd_num, arg_ptr) }
} else { } else {
@ -79,7 +79,7 @@ macro_rules! impl_ioctl_cmds {
} }
} }
unsafe fn new_builtin_cmd(cmd_num: BuiltinIoctlNum, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> { unsafe fn new_builtin_cmd(cmd_num: BuiltinIoctlNum, arg_ptr: *mut u8) -> Result<IoctlRawCmd<'a>> {
if cmd_num.require_arg() && arg_ptr.is_null() { if cmd_num.require_arg() && arg_ptr.is_null() {
return_errno!(EINVAL, "arg_ptr cannot be null"); return_errno!(EINVAL, "arg_ptr cannot be null");
} }
@ -90,43 +90,43 @@ macro_rules! impl_ioctl_cmds {
$( $(
BuiltinIoctlNum::$ioctl_name => { BuiltinIoctlNum::$ioctl_name => {
let arg = get_arg!($($ioctl_type_tt)*, arg_ptr); let arg = get_arg!($($ioctl_type_tt)*, arg_ptr);
IoctlCmd::$ioctl_name(arg) IoctlRawCmd::$ioctl_name(arg)
} }
)* )*
}; };
Ok(cmd) Ok(cmd)
} }
unsafe fn new_nonbuiltin_cmd(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> { unsafe fn new_nonbuiltin_cmd(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlRawCmd<'a>> {
let structured_cmd_num = StructuredIoctlNum::from_u32(cmd_num)?; let structured_cmd_num = StructuredIoctlNum::from_u32(cmd_num)?;
let inner_cmd = unsafe { NonBuiltinIoctlCmd::new(structured_cmd_num, arg_ptr)? }; 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 { pub fn arg_ptr(&self) -> *const u8 {
match self { 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 { pub fn arg_len(&self) -> usize {
match self { 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 { pub fn cmd_num(&self) -> u32 {
match self { 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(),
} }
} }
} }

@ -6,8 +6,13 @@
use super::*; 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}; pub use self::non_builtin::{NonBuiltinIoctlCmd, StructuredIoctlArgType, StructuredIoctlNum};
use crate::util::mem_util::from_user;
#[macro_use] #[macro_use]
mod macros; mod macros;
@ -18,7 +23,7 @@ mod non_builtin;
/// ///
/// By giving the names, numbers, and argument types of built-in ioctls, /// By giving the names, numbers, and argument types of built-in ioctls,
/// the macro below generates the corresponding code of `BuiltinIoctlNum` and /// 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 /// To add a new built-in ioctl, just follow the convention as shown
/// by existing built-in ioctls. /// 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 /// Sanity checks are mostly useful when the argument values are returned from
/// the untrusted host OS. /// the untrusted host OS.
impl<'a> IoctlCmd<'a> { impl<'a> IoctlRawCmd<'a> {
pub fn validate_arg_and_ret_vals(&self, ret: i32) -> Result<()> { pub fn to_safe_ioctlcmd(&self) -> Result<Box<dyn IoctlCmd>> {
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 { match self {
IoctlCmd::TIOCGWINSZ(winsize_ref) => { IoctlRawCmd::TCGETS(termios_mut) => {
// ws_row and ws_col are usually not zeros let cmd = cmd.downcast_ref::<TcGets>().unwrap();
if winsize_ref.ws_row == 0 || winsize_ref.ws_col == 0 { **termios_mut = *cmd.output().unwrap();
warn!( }
"window size: row: {:?}, col: {:?}", IoctlRawCmd::TIOCGWINSZ(winsize_mut) => {
winsize_ref.ws_row, winsize_ref.ws_col let cmd = cmd.downcast_ref::<GetWinSize>().unwrap();
); **winsize_mut = *cmd.output().unwrap();
}
IoctlRawCmd::FIONREAD(len_mut) => {
let cmd = cmd.downcast_ref::<GetReadBufLen>().unwrap();
**len_mut = *cmd.output().unwrap();
}
IoctlRawCmd::SIOCGIFCONF(ifconf_mut) => {
let cmd = cmd.downcast_ref::<GetIfConf>().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) => { IoctlRawCmd::SIOCGIFNAME(ifreq_mut)
if (**nread_ref < 0) { | IoctlRawCmd::SIOCGIFFLAGS(ifreq_mut)
return_errno!(EINVAL, "invalid data from host"); | 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::<GetIfReqWithRawCmd>().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<i32> { pub fn do_ioctl(fd: FileDesc, raw_cmd: &mut IoctlRawCmd<'_>) -> Result<i32> {
debug!("ioctl: fd: {}, cmd: {:?}", fd, cmd); debug!("ioctl: fd: {}, cmd: {:?}", fd, raw_cmd);
let current = current!(); let current = current!();
let file_ref = current.file(fd)?; let file_ref = current.file(fd)?;
let mut file_table = current.files().lock().unwrap(); let mut cmd = raw_cmd.to_safe_ioctlcmd()?;
let mut entry = file_table.get_entry_mut(fd)?;
match cmd { if cmd.is::<SetCloseOnExec>() {
IoctlCmd::FIONCLEX(_) => { let is_close_on_exec = cmd.downcast_ref::<SetCloseOnExec>().unwrap().input();
entry.set_close_on_spawn(false); 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); return Ok(0);
} }
IoctlCmd::FIOCLEX(_) => {
entry.set_close_on_spawn(true); file_ref.ioctl(cmd.as_mut())?;
return Ok(0); raw_cmd.copy_output_from_safe(cmd.as_ref());
} Ok(0)
_ => return file_ref.ioctl(cmd),
}
} }
extern "C" { extern "C" {

@ -69,7 +69,29 @@ impl<'a> NonBuiltinIoctlCmd<'a> {
pub fn arg_len(&self) -> usize { pub fn arg_len(&self) -> usize {
self.cmd_num.arg_size() 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)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct StructuredIoctlNum { pub struct StructuredIoctlNum {

@ -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::fsync::{do_fdatasync, do_fsync};
pub use self::getdents::{do_getdents, do_getdents64}; pub use self::getdents::{do_getdents, do_getdents64};
pub use self::ioctl::{ pub use self::ioctl::{
do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd, StructuredIoctlArgType, do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, GetIfConf, GetIfReqWithRawCmd, GetReadBufLen,
StructuredIoctlNum, GetWinSize, IfConf, IoctlCmd, IoctlRawCmd, NonBuiltinIoctlCmd, SetNonBlocking, SetWinSize,
StructuredIoctlArgType, StructuredIoctlNum, TcGets, TcSets,
}; };
pub use self::link::{do_linkat, LinkFlags}; pub use self::link::{do_linkat, LinkFlags};
pub use self::lseek::do_lseek; pub use self::lseek::do_lseek;

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::fs::IoctlCmd;
use crate::net::PollEventFlags; use crate::net::PollEventFlags;
use crate::process::do_getuid; use crate::process::do_getuid;
use rcore_fs::vfs::FallocateMode; use rcore_fs::vfs::FallocateMode;
@ -275,16 +276,13 @@ impl File for INodeFile {
self.unlock_range_lock(&range_lock) self.unlock_range_lock(&range_lock)
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
match cmd { match_ioctl_cmd_auto_error!(cmd, {
IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), cmd : NonBuiltinIoctlCmd => {
IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), self.inode.io_control(cmd.cmd_num().as_u32(), cmd.arg_ptr() as usize)?;
_ => {} },
}; });
let cmd_num = cmd.cmd_num(); Ok(())
let cmd_argp = cmd.arg_ptr() as usize;
self.inode.io_control(cmd_num, cmd_argp)?;
Ok(0)
} }
fn poll_new(&self) -> IoEvents { fn poll_new(&self) -> IoEvents {

@ -19,8 +19,9 @@ pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier};
pub use self::file::{File, FileRef}; pub use self::file::{File, FileRef};
pub use self::file_ops::{ pub use self::file_ops::{
occlum_ocall_ioctl, utimbuf_t, AccessMode, BuiltinIoctlNum, CreationFlags, FallocateFlags, occlum_ocall_ioctl, utimbuf_t, AccessMode, BuiltinIoctlNum, CreationFlags, FallocateFlags,
FileMode, IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, FileMode, GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, GetWinSize, IfConf, IoctlCmd,
STATUS_FLAGS_MASK, 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::file_table::{FileDesc, FileTable, FileTableEvent, FileTableNotifier};
pub use self::fs_ops::Statfs; pub use self::fs_ops::Statfs;

@ -1,8 +1,10 @@
use atomic::{Atomic, Ordering}; use atomic::{Atomic, Ordering};
use net::PollEventFlags;
use super::channel::{Channel, Consumer, Producer}; use super::channel::{Channel, Consumer, Producer};
use super::*; use super::*;
use net::PollEventFlags;
use crate::fs::{GetReadBufLen, IoctlCmd};
// TODO: Add F_SETPIPE_SZ in fcntl to dynamically change the size of pipe // 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. // 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 self
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
match cmd { match_ioctl_cmd_auto_error!(cmd, {
IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), cmd : GetReadBufLen => {
IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), let read_buf_len = self.consumer.ready_len();
IoctlCmd::FIONREAD(arg) => { cmd.set_output(read_buf_len as _);
let ready_len = self.get_ready_len().min(std::i32::MAX as usize) as i32; },
**arg = ready_len; });
return Ok(0); Ok(())
}
_ => return_errno!(ENOSYS, "not supported"),
};
unreachable!();
} }
} }
@ -209,13 +207,9 @@ impl File for PipeWriter {
self self
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
match cmd { match_ioctl_cmd_auto_error!(cmd, {});
IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), Ok(())
IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"),
_ => return_errno!(ENOSYS, "not supported"),
};
unreachable!();
} }
} }

@ -4,6 +4,9 @@ use core::cmp;
use std::io::{BufReader, LineWriter}; use std::io::{BufReader, LineWriter};
use std::sync::SgxMutex; use std::sync::SgxMutex;
use crate::fs::file_ops::{GetWinSize, SetWinSize, TcGets, TcSets};
use crate::fs::IoctlCmd;
macro_rules! try_libc_stdio { macro_rules! try_libc_stdio {
($ret: expr) => {{ ($ret: expr) => {{
let ret = unsafe { $ret }; let ret = unsafe { $ret };
@ -169,50 +172,6 @@ impl File for StdoutFile {
Ok(()) Ok(())
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
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<StatusFlags> { fn status_flags(&self) -> Result<StatusFlags> {
let ret = try_libc!(libc::ocall::fcntl_arg0( let ret = try_libc!(libc::ocall::fcntl_arg0(
self.host_fd() as i32, self.host_fd() as i32,
@ -359,48 +318,8 @@ impl File for StdinFile {
}) })
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
let host_stdin_fd = self.host_fd() as i32; stdio_ioctl(cmd, self.host_fd())
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 status_flags(&self) -> Result<StatusFlags> { fn status_flags(&self) -> Result<StatusFlags> {
@ -444,3 +363,22 @@ impl Debug for StdinFile {
unsafe impl Send for StdinFile {} unsafe impl Send for StdinFile {}
unsafe impl Sync 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(())
}

@ -673,7 +673,7 @@ pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result<isize> {
if argp.is_null() == false { if argp.is_null() == false {
from_user::check_mut_ptr(argp)?; from_user::check_mut_ptr(argp)?;
} }
IoctlCmd::new(cmd, argp)? IoctlRawCmd::new(cmd, argp)?
}; };
file_ops::do_ioctl(fd, &mut ioctl_cmd)?; file_ops::do_ioctl(fd, &mut ioctl_cmd)?;
Ok(0) Ok(0)

@ -115,11 +115,6 @@ impl File for TimerFile {
Ok(ret) Ok(ret)
} }
// TODO: implement ioctl
// fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
// self.ioctl_impl(cmd)
// }
fn access_mode(&self) -> Result<AccessMode> { fn access_mode(&self) -> Result<AccessMode> {
Ok(AccessMode::O_RDWR) Ok(AccessMode::O_RDWR)
} }