diff --git a/src/main/host/descriptor/eventfd.rs b/src/main/host/descriptor/eventfd.rs index 468d9d12f6..9a72d547a1 100644 --- a/src/main/host/descriptor/eventfd.rs +++ b/src/main/host/descriptor/eventfd.rs @@ -5,11 +5,13 @@ use crate::host::descriptor::{ FileMode, FileState, FileStatus, StateEventSource, StateListenerFilter, }; use crate::host::memory_manager::MemoryManager; +use crate::host::syscall::io::{IoVec, IoVecReader, IoVecWriter}; use crate::host::syscall_types::{PluginPtr, SyscallError, SyscallResult}; use crate::utility::callback_queue::{CallbackQueue, Handle}; -use crate::utility::stream_len::StreamLen; use crate::utility::HostTreePointer; +use std::io::{Read, Write}; + pub struct EventFd { counter: u64, is_semaphore_mode: bool, @@ -68,15 +70,14 @@ impl EventFd { Ok(()) } - pub fn read( + pub fn readv( &mut self, - mut bytes: W, + iovs: &[IoVec], offset: Option, + _flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - W: std::io::Write + std::io::Seek, - { + ) -> Result { // eventfds don't support seeking if offset.is_some() { return Err(Errno::ESPIPE.into()); @@ -85,9 +86,11 @@ impl EventFd { // eventfd(2): "Each successful read(2) returns an 8-byte integer" const NUM_BYTES: usize = 8; + let len: libc::size_t = iovs.iter().map(|x| x.len).sum(); + // this check doesn't guarentee that we can write all bytes since the stream length is only // a hint - if usize::try_from(bytes.stream_len_bp()?).unwrap() < NUM_BYTES { + if len < NUM_BYTES { log::trace!( "Reading from eventfd requires a buffer of at least {} bytes", NUM_BYTES @@ -100,31 +103,32 @@ impl EventFd { return Err(Errno::EWOULDBLOCK.into()); } + let mut writer = IoVecWriter::new(iovs, mem); + // behavior defined in `man 2 eventfd` if self.is_semaphore_mode { const ONE: [u8; NUM_BYTES] = 1u64.to_ne_bytes(); - bytes.write_all(&ONE)?; + writer.write_all(&ONE)?; self.counter -= 1; } else { let to_write: [u8; NUM_BYTES] = self.counter.to_ne_bytes(); - bytes.write_all(&to_write)?; + writer.write_all(&to_write)?; self.counter = 0; } self.update_state(cb_queue); - Ok(NUM_BYTES.into()) + Ok(NUM_BYTES.try_into().unwrap()) } - pub fn write( + pub fn writev( &mut self, - mut bytes: R, + iovs: &[IoVec], offset: Option, + _flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - R: std::io::Read + std::io::Seek, - { + ) -> Result { // eventfds don't support seeking if offset.is_some() { return Err(Errno::ESPIPE.into()); @@ -134,9 +138,11 @@ impl EventFd { // counter" const NUM_BYTES: usize = 8; + let len: libc::size_t = iovs.iter().map(|x| x.len).sum(); + // this check doesn't guarentee that we can read all bytes since the stream length is only // a hint - if usize::try_from(bytes.stream_len_bp()?).unwrap() < NUM_BYTES { + if len < NUM_BYTES { log::trace!( "Writing to eventfd requires a buffer with at least {} bytes", NUM_BYTES @@ -144,8 +150,15 @@ impl EventFd { return Err(Errno::EINVAL.into()); } + if iovs.len() > 1 { + // Linux doesn't seem to let you write to an eventfd with multiple iovecs + return Err(Errno::EINVAL.into()); + } + + let mut reader = IoVecReader::new(iovs, mem); + let mut read_buf = [0u8; NUM_BYTES]; - bytes.read_exact(&mut read_buf)?; + reader.read_exact(&mut read_buf)?; let value: u64 = u64::from_ne_bytes(read_buf); if value == u64::MAX { @@ -162,7 +175,7 @@ impl EventFd { self.counter += value; self.update_state(cb_queue); - Ok(NUM_BYTES.into()) + Ok(NUM_BYTES.try_into().unwrap()) } pub fn ioctl( diff --git a/src/main/host/descriptor/mod.rs b/src/main/host/descriptor/mod.rs index 6ab945a6ae..219e1c433a 100644 --- a/src/main/host/descriptor/mod.rs +++ b/src/main/host/descriptor/mod.rs @@ -6,6 +6,7 @@ use nix::fcntl::OFlag; use crate::core::worker; use crate::cshadow as c; use crate::host::memory_manager::MemoryManager; +use crate::host::syscall::io::IoVec; use crate::host::syscall_types::{PluginPtr, SyscallError, SyscallResult}; use crate::utility::callback_queue::{CallbackQueue, EventSource, Handle}; use crate::utility::{HostTreePointer, IsSend, IsSync}; @@ -430,15 +431,13 @@ impl FileRefMut<'_> { enum_passthrough!(self, (ptr), Pipe, EventFd, Socket; pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) ); - - enum_passthrough_generic!(self, (bytes, offset, cb_queue), Pipe, EventFd, Socket; - pub fn read(&mut self, bytes: W, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where W: std::io::Write + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket; + pub fn readv(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); - - enum_passthrough_generic!(self, (source, offset, cb_queue), Pipe, EventFd, Socket; - pub fn write(&mut self, source: R, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where R: std::io::Read + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Pipe, EventFd, Socket; + pub fn writev(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); } diff --git a/src/main/host/descriptor/pipe.rs b/src/main/host/descriptor/pipe.rs index 6a14a5408a..fb2fb3adf4 100644 --- a/src/main/host/descriptor/pipe.rs +++ b/src/main/host/descriptor/pipe.rs @@ -10,9 +10,9 @@ use crate::host::descriptor::{ FileMode, FileState, FileStatus, StateEventSource, StateListenerFilter, }; use crate::host::memory_manager::MemoryManager; +use crate::host::syscall::io::{IoVec, IoVecReader, IoVecWriter}; use crate::host::syscall_types::{PluginPtr, SyscallError, SyscallResult}; use crate::utility::callback_queue::{CallbackQueue, Handle}; -use crate::utility::stream_len::StreamLen; use crate::utility::HostTreePointer; pub struct Pipe { @@ -117,15 +117,14 @@ impl Pipe { Ok(()) } - pub fn read( + pub fn readv( &mut self, - mut bytes: W, + iovs: &[IoVec], offset: Option, + _flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - W: std::io::Write + std::io::Seek, - { + ) -> Result { // pipes don't support seeking if offset.is_some() { return Err(nix::errno::Errno::ESPIPE.into()); @@ -136,36 +135,39 @@ impl Pipe { return Err(nix::errno::Errno::EBADF.into()); } + let num_bytes_to_read: libc::size_t = iovs.iter().map(|x| x.len).sum(); + + let mut writer = IoVecWriter::new(iovs, mem); + let (num_copied, _num_removed_from_buf) = self .buffer .as_ref() .unwrap() .borrow_mut() - .read(&mut bytes, cb_queue)?; + .read(&mut writer, cb_queue)?; // the read would block if all: // 1. we could not read any bytes // 2. we were asked to read >0 bytes // 3. there are open descriptors that refer to the write end of the pipe if num_copied == 0 - && bytes.stream_len_bp()? != 0 + && num_bytes_to_read != 0 && self.buffer.as_ref().unwrap().borrow().num_writers() > 0 { Err(Errno::EWOULDBLOCK.into()) } else { - Ok(num_copied.into()) + Ok(num_copied.try_into().unwrap()) } } - pub fn write( + pub fn writev( &mut self, - mut bytes: R, + iovs: &[IoVec], offset: Option, + _flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - R: std::io::Read + std::io::Seek, - { + ) -> Result { // pipes don't support seeking if offset.is_some() { return Err(nix::errno::Errno::ESPIPE.into()); @@ -194,10 +196,12 @@ impl Pipe { } } - let len = bytes.stream_len_bp()? as usize; + let len: libc::size_t = iovs.iter().map(|x| x.len).sum(); + + let mut reader = IoVecReader::new(iovs, mem); - let result = match self.write_mode { - WriteMode::Stream => buffer.write_stream(bytes.by_ref(), len, cb_queue), + let num_copied = match self.write_mode { + WriteMode::Stream => buffer.write_stream(&mut reader, len, cb_queue)?, WriteMode::Packet => { let mut num_written = 0; @@ -207,18 +211,18 @@ impl Pipe { // if there are no more bytes to write (pipes don't support 0-length packets) if bytes_remaining == 0 { - break Ok(num_written); + break num_written; } // split the packet up into PIPE_BUF-sized packets let bytes_to_write = std::cmp::min(bytes_remaining, libc::PIPE_BUF); - if let Err(e) = buffer.write_packet(bytes.by_ref(), bytes_to_write, cb_queue) { + if let Err(e) = buffer.write_packet(&mut reader, bytes_to_write, cb_queue) { // if we've already written bytes, return those instead of an error if num_written > 0 { - break Ok(num_written); + break num_written; } - break Err(e); + return Err(e.into()); } num_written += bytes_to_write; @@ -226,7 +230,7 @@ impl Pipe { } }; - Ok(result?.into()) + Ok(num_copied.try_into().unwrap()) } pub fn ioctl( diff --git a/src/main/host/descriptor/socket/inet/legacy_tcp.rs b/src/main/host/descriptor/socket/inet/legacy_tcp.rs index d58cb6593c..caf599b4bb 100644 --- a/src/main/host/descriptor/socket/inet/legacy_tcp.rs +++ b/src/main/host/descriptor/socket/inet/legacy_tcp.rs @@ -15,7 +15,7 @@ use crate::host::descriptor::{ }; use crate::host::host::Host; use crate::host::memory_manager::MemoryManager; -use crate::host::syscall::io::write_partial; +use crate::host::syscall::io::{write_partial, IoVec}; use crate::host::syscall_types::{PluginPtr, SyscallError, TypedPluginPtr}; use crate::host::thread::ThreadId; use crate::network::net_namespace::NetworkNamespace; @@ -276,34 +276,32 @@ impl LegacyTcpSocket { Ok(0.into()) } - pub fn read( + pub fn readv( &mut self, - mut _bytes: W, + _iovs: &[IoVec], _offset: Option, + _flags: libc::c_int, + _mem: &mut MemoryManager, _cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - W: std::io::Write + std::io::Seek, - { + ) -> Result { // we could call LegacyTcpSocket::recvmsg() here, but for now we expect that there are no - // code paths that would call LegacyTcpSocket::read() since the read() syscall handler + // code paths that would call LegacyTcpSocket::readv() since the readv() syscall handler // should have called LegacyTcpSocket::recvmsg() instead - panic!("Called LegacyTcpSocket::read() on a TCP socket."); + panic!("Called LegacyTcpSocket::readv() on a TCP socket."); } - pub fn write( + pub fn writev( &mut self, - mut _bytes: R, + _iovs: &[IoVec], _offset: Option, + _flags: libc::c_int, + _mem: &mut MemoryManager, _cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - R: std::io::Read + std::io::Seek, - { + ) -> Result { // we could call LegacyTcpSocket::sendmsg() here, but for now we expect that there are no - // code paths that would call LegacyTcpSocket::write() since the write() syscall handler + // code paths that would call LegacyTcpSocket::writev() since the writev() syscall handler // should have called LegacyTcpSocket::sendmsg() instead - panic!("Called LegacyTcpSocket::write() on a TCP socket"); + panic!("Called LegacyTcpSocket::writev() on a TCP socket"); } pub fn sendmsg( diff --git a/src/main/host/descriptor/socket/inet/mod.rs b/src/main/host/descriptor/socket/inet/mod.rs index 7ead5607fa..f9d705589e 100644 --- a/src/main/host/descriptor/socket/inet/mod.rs +++ b/src/main/host/descriptor/socket/inet/mod.rs @@ -9,6 +9,7 @@ use crate::cshadow as c; use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs}; use crate::host::descriptor::{FileMode, FileState, FileStatus, OpenFile, SyscallResult}; use crate::host::memory_manager::MemoryManager; +use crate::host::syscall::io::IoVec; use crate::host::syscall_types::{PluginPtr, SyscallError}; use crate::network::net_namespace::NetworkNamespace; use crate::network::packet::Packet; @@ -246,15 +247,13 @@ impl InetSocketRefMut<'_> { enum_passthrough!(self, (ptr), LegacyTcp; pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) ); - - enum_passthrough_generic!(self, (bytes, offset, cb_queue), LegacyTcp; - pub fn read(&mut self, bytes: W, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where W: std::io::Write + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp; + pub fn readv(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); - - enum_passthrough_generic!(self, (source, offset, cb_queue), LegacyTcp; - pub fn write(&mut self, source: R, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where R: std::io::Read + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp; + pub fn writev(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); } diff --git a/src/main/host/descriptor/socket/mod.rs b/src/main/host/descriptor/socket/mod.rs index 5133eeeace..8c2dd570c5 100644 --- a/src/main/host/descriptor/socket/mod.rs +++ b/src/main/host/descriptor/socket/mod.rs @@ -242,15 +242,13 @@ impl SocketRefMut<'_> { enum_passthrough!(self, (ptr), Unix, Inet; pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) ); - - enum_passthrough_generic!(self, (bytes, offset, cb_queue), Unix, Inet; - pub fn read(&mut self, bytes: W, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where W: std::io::Write + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Unix, Inet; + pub fn readv(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); - - enum_passthrough_generic!(self, (source, offset, cb_queue), Unix, Inet; - pub fn write(&mut self, source: R, offset: Option, cb_queue: &mut CallbackQueue) -> SyscallResult - where R: std::io::Read + std::io::Seek + enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), Unix, Inet; + pub fn writev(&mut self, iovs: &[IoVec], offset: Option, flags: libc::c_int, + mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result ); } diff --git a/src/main/host/descriptor/socket/unix.rs b/src/main/host/descriptor/socket/unix.rs index 37afe3495c..13b732ba4c 100644 --- a/src/main/host/descriptor/socket/unix.rs +++ b/src/main/host/descriptor/socket/unix.rs @@ -146,34 +146,32 @@ impl UnixSocket { .bind(&mut socket_ref.common, socket, addr, rng) } - pub fn read( + pub fn readv( &mut self, - mut _bytes: W, + _iovs: &[IoVec], _offset: Option, + _flags: libc::c_int, + _mem: &mut MemoryManager, _cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - W: std::io::Write + std::io::Seek, - { + ) -> Result { // we could call UnixSocket::recvmsg() here, but for now we expect that there are no code - // paths that would call UnixSocket::read() since the read() syscall handler should have + // paths that would call UnixSocket::readv() since the readv() syscall handler should have // called UnixSocket::recvmsg() instead - panic!("Called UnixSocket::read() on a unix socket."); + panic!("Called UnixSocket::readv() on a unix socket."); } - pub fn write( + pub fn writev( &mut self, - mut _bytes: R, + _iovs: &[IoVec], _offset: Option, + _flags: libc::c_int, + _mem: &mut MemoryManager, _cb_queue: &mut CallbackQueue, - ) -> SyscallResult - where - R: std::io::Read + std::io::Seek, - { + ) -> Result { // we could call UnixSocket::sendmsg() here, but for now we expect that there are no code - // paths that would call UnixSocket::write() since the write() syscall handler should have + // paths that would call UnixSocket::writev() since the writev() syscall handler should have // called UnixSocket::sendmsg() instead - panic!("Called UnixSocket::write() on a unix socket"); + panic!("Called UnixSocket::writev() on a unix socket"); } pub fn sendmsg( diff --git a/src/main/host/syscall/handler/unistd.rs b/src/main/host/syscall/handler/unistd.rs index 0aa48cafcd..6cfc2afbdf 100644 --- a/src/main/host/syscall/handler/unistd.rs +++ b/src/main/host/syscall/handler/unistd.rs @@ -248,6 +248,13 @@ impl SyscallHandler { buf_size: libc::size_t, offset: Option, ) -> Result { + let mut mem = ctx.objs.process.memory_borrow_mut(); + + let iov = IoVec { + base: buf_ptr, + len: buf_size, + }; + // if it's a socket, call recvmsg() instead if let File::Socket(ref socket) = file { if offset.is_some() { @@ -255,19 +262,12 @@ impl SyscallHandler { return Err(Errno::ESPIPE.into()); } - let iov = IoVec { - base: buf_ptr, - len: buf_size, - }; - let args = RecvmsgArgs { iovs: &[iov], control_ptr: TypedPluginPtr::new::(PluginPtr::null(), 0), flags: 0, }; - let mut mem = ctx.objs.process.memory_borrow_mut(); - // call the socket's recvmsg(), and run any resulting events let RecvmsgReturn { bytes_read, .. } = CallbackQueue::queue_and_run(|cb_queue| { Socket::recvmsg(socket, args, &mut mem, cb_queue) @@ -279,11 +279,13 @@ impl SyscallHandler { let file_status = file.borrow().get_status(); let result = - // call the file's read(), and run any resulting events + // call the file's readv(), and run any resulting events CallbackQueue::queue_and_run(|cb_queue| { - file.borrow_mut().read( - ctx.objs.process.memory_borrow_mut().writer(TypedPluginPtr::new::(buf_ptr, buf_size)), + file.borrow_mut().readv( + &[iov], offset, + 0, + &mut mem, cb_queue, ) }); @@ -298,7 +300,7 @@ impl SyscallHandler { } let bytes_read = result?; - Ok(bytes_read.into()) + Ok(bytes_read) } #[log_syscall(/* rv */ libc::ssize_t, /* fd */ libc::c_int, @@ -414,6 +416,13 @@ impl SyscallHandler { buf_size: libc::size_t, offset: Option, ) -> Result { + let mut mem = ctx.objs.process.memory_borrow_mut(); + + let iov = IoVec { + base: buf_ptr, + len: buf_size, + }; + // if it's a socket, call sendmsg() instead if let File::Socket(ref socket) = file { if offset.is_some() { @@ -421,11 +430,6 @@ impl SyscallHandler { return Err(Errno::ESPIPE.into()); } - let iov = IoVec { - base: buf_ptr, - len: buf_size, - }; - let args = SendmsgArgs { addr: None, iovs: &[iov], @@ -433,8 +437,6 @@ impl SyscallHandler { flags: 0, }; - let mut mem = ctx.objs.process.memory_borrow_mut(); - // call the socket's sendmsg(), and run any resulting events let bytes_written = CallbackQueue::queue_and_run(|cb_queue| { Socket::sendmsg(socket, args, &mut mem, cb_queue) @@ -446,11 +448,13 @@ impl SyscallHandler { let file_status = file.borrow().get_status(); let result = - // call the file's write(), and run any resulting events + // call the file's writev(), and run any resulting events CallbackQueue::queue_and_run(|cb_queue| { - file.borrow_mut().write( - ctx.objs.process.memory_borrow().reader(TypedPluginPtr::new::(buf_ptr, buf_size)), + file.borrow_mut().writev( + &[iov], offset, + 0, + &mut mem, cb_queue, ) }); @@ -465,7 +469,7 @@ impl SyscallHandler { }; let bytes_written = result?; - Ok(bytes_written.into()) + Ok(bytes_written) } #[log_syscall(/* rv */ libc::c_int, /* pipefd */ [libc::c_int; 2])] diff --git a/src/main/utility/enum_passthrough.rs b/src/main/utility/enum_passthrough.rs index 3d6ba28a62..c42b32eea4 100644 --- a/src/main/utility/enum_passthrough.rs +++ b/src/main/utility/enum_passthrough.rs @@ -40,6 +40,8 @@ enum_passthrough_generic!(self, (bytes, offset, cb_queue), Pipe, Socket; ); ``` **/ +// This is currently unused, but keeping around for now since we may want it again in the future. +#[allow(unused_macros)] macro_rules! enum_passthrough_generic { ($self:ident, $args2:tt, $($variant:ident),+; $(#[$($mac:tt)+])? $v:vis fn $name:ident <$($generics:ident),+> $args:tt $(-> $($rv:tt)+)?) => { $(#[$($mac)+])?