1016 lines
30 KiB
Rust
1016 lines
30 KiB
Rust
use super::*;
|
|
|
|
use std::mem::MaybeUninit;
|
|
use std::time::Duration;
|
|
|
|
use super::io_multiplexing::{AsEpollFile, EpollCtl, EpollFile, EpollFlags, FdSetExt, PollFd};
|
|
use fs::{CreationFlags, File, FileDesc, FileRef};
|
|
use misc::resource_t;
|
|
use process::Process;
|
|
use signal::{sigset_t, MaskOp, SigSet, SIGKILL, SIGSTOP};
|
|
use std::convert::TryFrom;
|
|
use time::{timespec_t, timeval_t};
|
|
use util::mem_util::from_user;
|
|
|
|
pub fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize> {
|
|
let sock_domain = AddressFamily::try_from(domain as u16)?;
|
|
let file_flags = FileFlags::from_bits_truncate(socket_type);
|
|
let sock_type = SocketType::try_from(socket_type & (!file_flags.bits()))?;
|
|
|
|
let file_ref: Arc<dyn File> = match sock_domain {
|
|
AddressFamily::LOCAL => {
|
|
let unix_socket = unix_socket(sock_type, file_flags, protocol)?;
|
|
Arc::new(unix_socket)
|
|
}
|
|
_ => {
|
|
let socket = HostSocket::new(sock_domain, sock_type, file_flags, protocol)?;
|
|
Arc::new(socket)
|
|
}
|
|
};
|
|
|
|
let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
|
|
let fd = current!().add_file(file_ref, close_on_spawn);
|
|
Ok(fd as isize)
|
|
}
|
|
|
|
pub fn do_bind(fd: c_int, addr: *const libc::sockaddr, addr_len: libc::socklen_t) -> Result<isize> {
|
|
if addr.is_null() || addr_len == 0 {
|
|
return_errno!(EINVAL, "no address is specified");
|
|
}
|
|
from_user::check_array(addr as *const u8, addr_len as usize)?;
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let sock_addr = unsafe { SockAddr::try_from_raw(addr, addr_len)? };
|
|
trace!("bind to addr: {:?}", sock_addr);
|
|
socket.bind(&sock_addr)?;
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let mut unix_addr = unsafe { UnixAddr::try_from_raw(addr, addr_len)? };
|
|
trace!("bind to addr: {:?}", unix_addr);
|
|
unix_socket.bind(&mut unix_addr)?;
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn do_listen(fd: c_int, backlog: c_int) -> Result<isize> {
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
socket.listen(backlog)?;
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
unix_socket.listen(backlog)?;
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn do_connect(
|
|
fd: c_int,
|
|
addr: *const libc::sockaddr,
|
|
addr_len: libc::socklen_t,
|
|
) -> Result<isize> {
|
|
// For SOCK_DGRAM sockets not initiated in connection-mode,
|
|
// if address is a null address for the protocol,
|
|
// the socket's peer address shall be reset.
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_array(addr as *const u8, addr_len as usize)?;
|
|
}
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let addr_option = if addr_set {
|
|
Some(unsafe { SockAddr::try_from_raw(addr, addr_len)? })
|
|
} else {
|
|
None
|
|
};
|
|
|
|
socket.connect(&addr_option)?;
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
// TODO: support AF_UNSPEC address for datagram socket use
|
|
let addr = if addr_set {
|
|
unsafe { UnixAddr::try_from_raw(addr, addr_len)? }
|
|
} else {
|
|
return_errno!(EINVAL, "invalid address");
|
|
};
|
|
|
|
unix_socket.connect(&addr)?;
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn do_accept(
|
|
fd: c_int,
|
|
addr: *mut libc::sockaddr,
|
|
addr_len: *mut libc::socklen_t,
|
|
) -> Result<isize> {
|
|
do_accept4(fd, addr, addr_len, 0)
|
|
}
|
|
|
|
pub fn do_accept4(
|
|
fd: c_int,
|
|
addr: *mut libc::sockaddr,
|
|
addr_len: *mut libc::socklen_t,
|
|
flags: c_int,
|
|
) -> Result<isize> {
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_ptr(addr_len)?;
|
|
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
|
|
}
|
|
|
|
let file_flags = FileFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
|
|
let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let (new_socket_file, sock_addr_option) = socket.accept(file_flags)?;
|
|
let new_file_ref: Arc<dyn File> = Arc::new(new_socket_file);
|
|
let new_fd = current!().add_file(new_file_ref, close_on_spawn);
|
|
|
|
if addr_set {
|
|
if let Some(sock_addr) = sock_addr_option {
|
|
let mut buf =
|
|
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
|
|
sock_addr.copy_to_slice(&mut buf);
|
|
unsafe {
|
|
*addr_len = sock_addr.len() as u32;
|
|
}
|
|
} else {
|
|
unsafe {
|
|
*addr_len = 0;
|
|
}
|
|
}
|
|
}
|
|
Ok(new_fd as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let (new_socket_file, sock_addr_option) = unix_socket.accept(file_flags)?;
|
|
let new_file_ref: Arc<dyn File> = Arc::new(new_socket_file);
|
|
let new_fd = current!().add_file(new_file_ref, close_on_spawn);
|
|
|
|
if addr_set {
|
|
if let Some(sock_addr) = sock_addr_option {
|
|
let mut buf =
|
|
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
|
|
sock_addr.copy_to_slice(&mut buf);
|
|
unsafe {
|
|
*addr_len = sock_addr.raw_len() as u32;
|
|
}
|
|
} else {
|
|
unsafe {
|
|
*addr_len = 0;
|
|
}
|
|
}
|
|
}
|
|
Ok(new_fd as isize)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
}
|
|
|
|
pub fn do_shutdown(fd: c_int, how: c_int) -> Result<isize> {
|
|
debug!("shutdown: fd: {}, how: {}", fd, how);
|
|
let how = HowToShut::try_from_raw(how)?;
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
socket.shutdown(how)?;
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
unix_socket.shutdown(how)?;
|
|
} else {
|
|
return_errno!(EBADF, "not a host socket")
|
|
}
|
|
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn do_setsockopt(
|
|
fd: c_int,
|
|
level: c_int,
|
|
optname: c_int,
|
|
optval: *const c_void,
|
|
optlen: libc::socklen_t,
|
|
) -> Result<isize> {
|
|
debug!(
|
|
"setsockopt: fd: {}, level: {}, optname: {}, optval: {:?}, optlen: {:?}",
|
|
fd, level, optname, optval, optlen
|
|
);
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let ret = try_libc!(libc::ocall::setsockopt(
|
|
socket.raw_host_fd() as i32,
|
|
level,
|
|
optname,
|
|
optval,
|
|
optlen
|
|
));
|
|
Ok(ret as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
warn!("setsockopt for unix socket is unimplemented");
|
|
Ok(0)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
pub fn do_getsockopt(
|
|
fd: c_int,
|
|
level: c_int,
|
|
optname: c_int,
|
|
optval: *mut c_void,
|
|
optlen: *mut libc::socklen_t,
|
|
) -> Result<isize> {
|
|
debug!(
|
|
"getsockopt: fd: {}, level: {}, optname: {}, optval: {:?}, optlen: {:?}",
|
|
fd, level, optname, optval, optlen
|
|
);
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
let socket = file_ref.as_host_socket();
|
|
|
|
if let Ok(socket) = socket {
|
|
let ret = try_libc!(libc::ocall::getsockopt(
|
|
socket.raw_host_fd() as i32,
|
|
level,
|
|
optname,
|
|
optval,
|
|
optlen
|
|
));
|
|
Ok(ret as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
warn!("getsockopt for unix socket is unimplemented");
|
|
Ok(0)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
pub fn do_getpeername(
|
|
fd: c_int,
|
|
addr: *mut libc::sockaddr,
|
|
addr_len: *mut libc::socklen_t,
|
|
) -> Result<isize> {
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_ptr(addr_len)?;
|
|
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
|
|
} else {
|
|
return Ok(0);
|
|
}
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let ret = try_libc!(libc::ocall::getpeername(
|
|
socket.raw_host_fd() as i32,
|
|
addr,
|
|
addr_len
|
|
));
|
|
Ok(ret as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let name = unix_socket.peer_addr()?;
|
|
let mut dst = unsafe {
|
|
std::slice::from_raw_parts_mut(addr as *mut _ as *mut u8, *addr_len as usize)
|
|
};
|
|
name.copy_to_slice(dst);
|
|
unsafe {
|
|
*addr_len = name.raw_len() as u32;
|
|
}
|
|
Ok(0)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
pub fn do_getsockname(
|
|
fd: c_int,
|
|
addr: *mut libc::sockaddr,
|
|
addr_len: *mut libc::socklen_t,
|
|
) -> Result<isize> {
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_ptr(addr_len)?;
|
|
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
|
|
} else {
|
|
return Ok(0);
|
|
}
|
|
|
|
if unsafe { *addr_len } < std::mem::size_of::<libc::sa_family_t>() as u32 {
|
|
return_errno!(EINVAL, "input length is too short");
|
|
}
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let ret = try_libc!(libc::ocall::getsockname(
|
|
socket.raw_host_fd() as i32,
|
|
addr,
|
|
addr_len
|
|
));
|
|
Ok(ret as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let name_opt = unix_socket.addr();
|
|
if let Some(name) = name_opt {
|
|
let mut dst = unsafe {
|
|
std::slice::from_raw_parts_mut(addr as *mut _ as *mut u8, *addr_len as usize)
|
|
};
|
|
name.copy_to_slice(dst);
|
|
unsafe {
|
|
*addr_len = name.raw_len() as u32;
|
|
}
|
|
} else {
|
|
unsafe {
|
|
(*addr).sa_family = AddressFamily::LOCAL as u16;
|
|
*addr_len = 2;
|
|
}
|
|
}
|
|
Ok(0)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
}
|
|
|
|
pub fn do_sendto(
|
|
fd: c_int,
|
|
base: *const c_void,
|
|
len: size_t,
|
|
flags: c_int,
|
|
addr: *const libc::sockaddr,
|
|
addr_len: libc::socklen_t,
|
|
) -> Result<isize> {
|
|
if len == 0 {
|
|
return Ok(0);
|
|
}
|
|
|
|
if addr.is_null() ^ (addr_len == 0) {
|
|
return_errno!(EINVAL, "addr and ddr_len should be both null");
|
|
}
|
|
|
|
from_user::check_array(base as *const u8, len)?;
|
|
let buf = unsafe { std::slice::from_raw_parts(base as *const u8, len as usize) };
|
|
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_mut_array(addr as *mut u8, addr_len as usize)?;
|
|
}
|
|
|
|
let send_flags = SendFlags::from_bits(flags).unwrap();
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let addr_option = if addr_set {
|
|
Some(unsafe { SockAddr::try_from_raw(addr, addr_len)? })
|
|
} else {
|
|
None
|
|
};
|
|
|
|
socket
|
|
.sendto(buf, send_flags, &addr_option)
|
|
.map(|u| u as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let addr_option = if addr_set {
|
|
Some(unsafe { UnixAddr::try_from_raw(addr, addr_len)? })
|
|
} else {
|
|
None
|
|
};
|
|
|
|
unix_socket
|
|
.sendto(buf, send_flags, &addr_option)
|
|
.map(|u| u as isize)
|
|
} else {
|
|
return_errno!(EBADF, "unsupported file type");
|
|
}
|
|
}
|
|
|
|
pub fn do_recvfrom(
|
|
fd: c_int,
|
|
base: *mut c_void,
|
|
len: size_t,
|
|
flags: c_int,
|
|
addr: *mut libc::sockaddr,
|
|
addr_len: *mut libc::socklen_t,
|
|
) -> Result<isize> {
|
|
if addr.is_null() ^ addr_len.is_null() {
|
|
return_errno!(EINVAL, "addr and ddr_len should be both null");
|
|
}
|
|
|
|
from_user::check_array(base as *mut u8, len)?;
|
|
let mut buf = unsafe { std::slice::from_raw_parts_mut(base as *mut u8, len as usize) };
|
|
|
|
// MSG_CTRUNC is a return flag but linux allows it to be set on input flags.
|
|
// We just ignore it.
|
|
let recv_flags = RecvFlags::from_bits(flags & !(MsgHdrFlags::MSG_CTRUNC.bits()))
|
|
.ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
|
|
|
|
let addr_set: bool = !addr.is_null();
|
|
if addr_set {
|
|
from_user::check_ptr(addr_len)?;
|
|
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
|
|
}
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let (data_len, sock_addr_option) = socket.recvfrom(buf, recv_flags)?;
|
|
if addr_set {
|
|
if let Some(sock_addr) = sock_addr_option {
|
|
let mut buf =
|
|
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
|
|
sock_addr.copy_to_slice(&mut buf);
|
|
unsafe {
|
|
*addr_len = sock_addr.len() as u32;
|
|
}
|
|
} else {
|
|
unsafe {
|
|
*addr_len = 0;
|
|
}
|
|
}
|
|
}
|
|
Ok(data_len as isize)
|
|
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
let (data_len, sock_addr_option) = unix_socket.recvfrom(buf, recv_flags)?;
|
|
if addr_set {
|
|
if let Some(sock_addr) = sock_addr_option {
|
|
let mut buf =
|
|
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
|
|
sock_addr.copy_to_slice(&mut buf);
|
|
unsafe {
|
|
*addr_len = sock_addr.raw_len() as u32;
|
|
}
|
|
} else {
|
|
unsafe {
|
|
*addr_len = 0;
|
|
}
|
|
}
|
|
}
|
|
Ok(data_len as isize)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket");
|
|
}
|
|
}
|
|
|
|
pub fn do_socketpair(
|
|
domain: c_int,
|
|
socket_type: c_int,
|
|
protocol: c_int,
|
|
sv: *mut c_int,
|
|
) -> Result<isize> {
|
|
let mut sock_pair = unsafe {
|
|
from_user::check_mut_array(sv, 2)?;
|
|
std::slice::from_raw_parts_mut(sv as *mut u32, 2)
|
|
};
|
|
|
|
let file_flags = FileFlags::from_bits_truncate(socket_type);
|
|
let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
|
|
let sock_type = SocketType::try_from(socket_type & (!file_flags.bits()))?;
|
|
|
|
let domain = AddressFamily::try_from(domain as u16)?;
|
|
if (domain == AddressFamily::LOCAL) {
|
|
let (client_socket, server_socket) = socketpair(sock_type, file_flags, protocol as i32)?;
|
|
|
|
let current = current!();
|
|
let mut files = current.files().lock().unwrap();
|
|
sock_pair[0] = files.put(Arc::new(client_socket), close_on_spawn);
|
|
sock_pair[1] = files.put(Arc::new(server_socket), close_on_spawn);
|
|
|
|
debug!("socketpair: ({}, {})", sock_pair[0], sock_pair[1]);
|
|
Ok(0)
|
|
} else {
|
|
return_errno!(EAFNOSUPPORT, "domain not supported")
|
|
}
|
|
}
|
|
|
|
pub fn do_sendmsg(fd: c_int, msg_ptr: *const msghdr, flags_c: c_int) -> Result<isize> {
|
|
debug!(
|
|
"sendmsg: fd: {}, msg: {:?}, flags: 0x{:x}",
|
|
fd, msg_ptr, flags_c
|
|
);
|
|
|
|
let msg_hdr = {
|
|
let msg_hdr_c = {
|
|
from_user::check_ptr(msg_ptr)?;
|
|
let msg_hdr_c = unsafe { &*msg_ptr };
|
|
msg_hdr_c.check_member_ptrs()?;
|
|
msg_hdr_c
|
|
};
|
|
unsafe { MsgHdr::from_c(&msg_hdr_c)? }
|
|
};
|
|
|
|
let flags = SendFlags::from_bits_truncate(flags_c);
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
socket
|
|
.sendmsg(&msg_hdr, flags)
|
|
.map(|bytes_sent| bytes_sent as isize)
|
|
} else if let Ok(socket) = file_ref.as_unix_socket() {
|
|
socket
|
|
.sendmsg(&msg_hdr, flags)
|
|
.map(|bytes_sent| bytes_sent as isize)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
pub fn do_recvmsg(fd: c_int, msg_mut_ptr: *mut msghdr_mut, flags_c: c_int) -> Result<isize> {
|
|
debug!(
|
|
"recvmsg: fd: {}, msg: {:?}, flags: 0x{:x}",
|
|
fd, msg_mut_ptr, flags_c
|
|
);
|
|
|
|
let mut msg_hdr_mut = {
|
|
let msg_hdr_mut_c = {
|
|
from_user::check_mut_ptr(msg_mut_ptr)?;
|
|
let msg_hdr_mut_c = unsafe { &mut *msg_mut_ptr };
|
|
msg_hdr_mut_c.check_member_ptrs()?;
|
|
msg_hdr_mut_c
|
|
};
|
|
unsafe { MsgHdrMut::from_c(msg_hdr_mut_c) }?
|
|
};
|
|
|
|
let flags = RecvFlags::from_bits_truncate(flags_c);
|
|
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
socket
|
|
.recvmsg(&mut msg_hdr_mut, flags)
|
|
.map(|bytes_recvd| bytes_recvd as isize)
|
|
} else if let Ok(socket) = file_ref.as_unix_socket() {
|
|
socket
|
|
.recvmsg(&mut msg_hdr_mut, flags)
|
|
.map(|bytes_recvd| bytes_recvd as isize)
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
pub fn do_sendmmsg(
|
|
fd: c_int,
|
|
msgvec_ptr: *mut mmsghdr,
|
|
vlen: c_uint,
|
|
flags_c: c_int,
|
|
) -> Result<isize> {
|
|
debug!(
|
|
"sendmmsg: fd: {}, msg: {:?}, flags: 0x{:x}",
|
|
fd, msgvec_ptr, flags_c
|
|
);
|
|
|
|
from_user::check_ptr(msgvec_ptr)?;
|
|
|
|
let mut msgvec = unsafe { std::slice::from_raw_parts_mut(msgvec_ptr, vlen as usize) };
|
|
let flags = SendFlags::from_bits_truncate(flags_c);
|
|
let file_ref = current!().file(fd as FileDesc)?;
|
|
|
|
if let Ok(socket) = file_ref.as_host_socket() {
|
|
let mut send_count = 0;
|
|
for mmsg in (msgvec) {
|
|
if !mmsg.msg_hdr.check_member_ptrs().is_ok() {
|
|
break;
|
|
}
|
|
|
|
let msg = unsafe {
|
|
if let Ok(msg) = MsgHdr::from_c({ &mmsg.msg_hdr }) {
|
|
msg
|
|
} else {
|
|
break;
|
|
}
|
|
};
|
|
|
|
if socket
|
|
.sendmsg(&msg, flags)
|
|
.map(|bytes_sent| {
|
|
mmsg.msg_len = bytes_sent as u32;
|
|
mmsg.msg_len
|
|
})
|
|
.is_ok()
|
|
{
|
|
send_count += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(send_count as isize)
|
|
} else if let Ok(socket) = file_ref.as_unix_socket() {
|
|
return_errno!(EOPNOTSUPP, "does not support unix socket")
|
|
} else {
|
|
return_errno!(ENOTSOCK, "not a socket")
|
|
}
|
|
}
|
|
|
|
#[allow(non_camel_case_types)]
|
|
trait c_msghdr_ext {
|
|
fn check_member_ptrs(&self) -> Result<()>;
|
|
}
|
|
|
|
impl c_msghdr_ext for msghdr {
|
|
// TODO: implement this!
|
|
fn check_member_ptrs(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
/*
|
|
///user space check
|
|
pub unsafe fn check_from_user(user_hdr: *const msghdr) -> Result<()> {
|
|
Self::check_pointer(user_hdr, from_user::check_ptr)
|
|
}
|
|
|
|
///Check msghdr ptr
|
|
pub unsafe fn check_pointer(
|
|
user_hdr: *const msghdr,
|
|
check_ptr: fn(*const u8) -> Result<()>,
|
|
) -> Result<()> {
|
|
check_ptr(user_hdr as *const u8)?;
|
|
|
|
if (*user_hdr).msg_name.is_null() ^ ((*user_hdr).msg_namelen == 0) {
|
|
return_errno!(EINVAL, "name length is invalid");
|
|
}
|
|
|
|
if (*user_hdr).msg_iov.is_null() ^ ((*user_hdr).msg_iovlen == 0) {
|
|
return_errno!(EINVAL, "iov length is invalid");
|
|
}
|
|
|
|
if (*user_hdr).msg_control.is_null() ^ ((*user_hdr).msg_controllen == 0) {
|
|
return_errno!(EINVAL, "control length is invalid");
|
|
}
|
|
|
|
if !(*user_hdr).msg_name.is_null() {
|
|
check_ptr((*user_hdr).msg_name as *const u8)?;
|
|
}
|
|
|
|
if !(*user_hdr).msg_iov.is_null() {
|
|
check_ptr((*user_hdr).msg_iov as *const u8)?;
|
|
let iov_slice = slice::from_raw_parts((*user_hdr).msg_iov, (*user_hdr).msg_iovlen);
|
|
for iov in iov_slice {
|
|
check_ptr(iov.iov_base as *const u8)?;
|
|
}
|
|
}
|
|
|
|
if !(*user_hdr).msg_control.is_null() {
|
|
check_ptr((*user_hdr).msg_control as *const u8)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
*/
|
|
}
|
|
|
|
impl c_msghdr_ext for msghdr_mut {
|
|
fn check_member_ptrs(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn do_select(
|
|
nfds: c_int,
|
|
readfds: *mut libc::fd_set,
|
|
writefds: *mut libc::fd_set,
|
|
exceptfds: *mut libc::fd_set,
|
|
timeout: *mut timeval_t,
|
|
) -> Result<isize> {
|
|
let nfds = {
|
|
let soft_rlimit_nofile = current!()
|
|
.rlimits()
|
|
.lock()
|
|
.unwrap()
|
|
.get(resource_t::RLIMIT_NOFILE)
|
|
.get_cur();
|
|
if nfds < 0 || nfds > libc::FD_SETSIZE as i32 || nfds as u64 > soft_rlimit_nofile {
|
|
return_errno!(
|
|
EINVAL,
|
|
"nfds is negative or exceeds the resource limit or FD_SETSIZE"
|
|
);
|
|
}
|
|
nfds as FileDesc
|
|
};
|
|
|
|
let mut timeout_c = if !timeout.is_null() {
|
|
from_user::check_ptr(timeout)?;
|
|
let timeval = unsafe { &mut *timeout };
|
|
timeval.validate()?;
|
|
Some(timeval)
|
|
} else {
|
|
None
|
|
};
|
|
let mut timeout = timeout_c.as_ref().map(|timeout_c| timeout_c.as_duration());
|
|
|
|
let readfds = if !readfds.is_null() {
|
|
from_user::check_mut_ptr(readfds)?;
|
|
Some(unsafe { &mut *readfds })
|
|
} else {
|
|
None
|
|
};
|
|
let writefds = if !writefds.is_null() {
|
|
from_user::check_mut_ptr(writefds)?;
|
|
Some(unsafe { &mut *writefds })
|
|
} else {
|
|
None
|
|
};
|
|
let exceptfds = if !exceptfds.is_null() {
|
|
from_user::check_mut_ptr(exceptfds)?;
|
|
Some(unsafe { &mut *exceptfds })
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let ret = io_multiplexing::do_select(nfds, readfds, writefds, exceptfds, timeout.as_mut());
|
|
|
|
if let Some(timeout_c) = timeout_c {
|
|
*timeout_c = timeout.unwrap().into();
|
|
}
|
|
|
|
ret
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C)]
|
|
pub struct sigset_argpack {
|
|
ss: *const sigset_t,
|
|
ss_len: size_t,
|
|
}
|
|
|
|
pub fn do_pselect6(
|
|
nfds: c_int,
|
|
readfds: *mut libc::fd_set,
|
|
writefds: *mut libc::fd_set,
|
|
exceptfds: *mut libc::fd_set,
|
|
timeout: *mut timespec_t,
|
|
sig_data: *const sigset_argpack,
|
|
) -> Result<isize> {
|
|
let mut is_set_sig = false;
|
|
let mut prev_mask = SigSet::default();
|
|
let thread = current!();
|
|
|
|
// Set signal mask
|
|
if !sig_data.is_null() {
|
|
from_user::check_ptr(sig_data)?;
|
|
let user_sig_data = unsafe { &*(sig_data) };
|
|
|
|
is_set_sig = !user_sig_data.ss.is_null();
|
|
if is_set_sig {
|
|
if user_sig_data.ss_len != std::mem::size_of::<sigset_t>() {
|
|
return_errno!(EINVAL, "unexpected sigset size");
|
|
}
|
|
let update_mask = {
|
|
let sigset = user_sig_data.ss;
|
|
from_user::check_ptr(sigset)?;
|
|
let set = unsafe { &*sigset };
|
|
let mut set = SigSet::from_c(unsafe { *sigset });
|
|
// According to man pages, "it is not possible to block SIGKILL or SIGSTOP.
|
|
// Attempts to do so are silently ignored."
|
|
set -= SIGKILL;
|
|
set -= SIGSTOP;
|
|
set
|
|
};
|
|
let mut curr_mask = thread.sig_mask().write().unwrap();
|
|
prev_mask = *curr_mask;
|
|
*curr_mask = update_mask;
|
|
}
|
|
}
|
|
|
|
let nfds = {
|
|
let soft_rlimit_nofile = current!()
|
|
.rlimits()
|
|
.lock()
|
|
.unwrap()
|
|
.get(resource_t::RLIMIT_NOFILE)
|
|
.get_cur();
|
|
if nfds < 0 || nfds > libc::FD_SETSIZE as i32 || nfds as u64 > soft_rlimit_nofile {
|
|
return_errno!(
|
|
EINVAL,
|
|
"nfds is negative or exceeds the resource limit or FD_SETSIZE"
|
|
);
|
|
}
|
|
nfds as FileDesc
|
|
};
|
|
|
|
let mut timeout_c = if !timeout.is_null() {
|
|
from_user::check_ptr(timeout)?;
|
|
let timeval = unsafe { &mut *timeout };
|
|
timeval.validate()?;
|
|
Some(timeval)
|
|
} else {
|
|
None
|
|
};
|
|
let mut timeout = timeout_c.as_ref().map(|timeout_c| timeout_c.as_duration());
|
|
|
|
let readfds = if !readfds.is_null() {
|
|
from_user::check_mut_ptr(readfds)?;
|
|
Some(unsafe { &mut *readfds })
|
|
} else {
|
|
None
|
|
};
|
|
let writefds = if !writefds.is_null() {
|
|
from_user::check_mut_ptr(writefds)?;
|
|
Some(unsafe { &mut *writefds })
|
|
} else {
|
|
None
|
|
};
|
|
let exceptfds = if !exceptfds.is_null() {
|
|
from_user::check_mut_ptr(exceptfds)?;
|
|
Some(unsafe { &mut *exceptfds })
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let ret = io_multiplexing::do_select(nfds, readfds, writefds, exceptfds, timeout.as_mut());
|
|
|
|
if let Some(timeout_c) = timeout_c {
|
|
*timeout_c = timeout.unwrap().into();
|
|
}
|
|
|
|
// Restore the original signal mask
|
|
if is_set_sig {
|
|
let mut curr_mask = thread.sig_mask().write().unwrap();
|
|
*curr_mask = prev_mask;
|
|
}
|
|
|
|
ret
|
|
}
|
|
|
|
pub fn do_ppoll(
|
|
fds: *mut libc::pollfd,
|
|
nfds: libc::nfds_t,
|
|
timeout_ts: *const timespec_t,
|
|
sigmask: *const sigset_t,
|
|
) -> Result<isize> {
|
|
let mut timeout = if timeout_ts.is_null() {
|
|
None
|
|
} else {
|
|
from_user::check_ptr(timeout_ts)?;
|
|
let timeout_ts = unsafe { &*timeout_ts };
|
|
Some(timeout_ts.as_duration())
|
|
};
|
|
if !sigmask.is_null() {
|
|
warn!("ppoll sigmask is not supported!");
|
|
}
|
|
do_poll_common(fds, nfds, timeout.as_mut(), None)
|
|
}
|
|
|
|
pub fn do_poll(fds: *mut libc::pollfd, nfds: libc::nfds_t, timeout_ms: c_int) -> Result<isize> {
|
|
let mut timeout = if timeout_ms >= 0 {
|
|
Some(Duration::from_millis(timeout_ms as u64))
|
|
} else {
|
|
None
|
|
};
|
|
do_poll_common(fds, nfds, timeout.as_mut(), None)
|
|
}
|
|
|
|
fn do_poll_common(
|
|
fds: *mut libc::pollfd,
|
|
nfds: libc::nfds_t,
|
|
timeout: Option<&mut Duration>,
|
|
sigmask: Option<SigSet>,
|
|
) -> Result<isize> {
|
|
// It behaves like sleep when fds is null and nfds is zero.
|
|
if !fds.is_null() || nfds != 0 {
|
|
from_user::check_mut_array(fds, nfds as usize)?;
|
|
}
|
|
|
|
let soft_rlimit_nofile = current!()
|
|
.rlimits()
|
|
.lock()
|
|
.unwrap()
|
|
.get(resource_t::RLIMIT_NOFILE)
|
|
.get_cur();
|
|
// TODO: Check nfds against the size of the stack used in ocall to prevent stack overflow
|
|
if nfds > soft_rlimit_nofile {
|
|
return_errno!(EINVAL, "The nfds value exceeds the RLIMIT_NOFILE value.");
|
|
}
|
|
|
|
let raw_poll_fds = unsafe { std::slice::from_raw_parts_mut(fds, nfds as usize) };
|
|
let poll_fds: Vec<PollFd> = raw_poll_fds
|
|
.iter()
|
|
.map(|raw| PollFd::from_raw(raw))
|
|
.collect();
|
|
|
|
let count = io_multiplexing::do_poll_new(&poll_fds, timeout)?;
|
|
|
|
for (raw_poll_fd, poll_fd) in raw_poll_fds.iter_mut().zip(poll_fds.iter()) {
|
|
raw_poll_fd.revents = poll_fd.revents().get().to_raw() as i16;
|
|
}
|
|
Ok(count as isize)
|
|
}
|
|
|
|
pub fn do_epoll_create(size: c_int) -> Result<isize> {
|
|
if size <= 0 {
|
|
return_errno!(EINVAL, "size is not positive");
|
|
}
|
|
do_epoll_create1(0)
|
|
}
|
|
|
|
pub fn do_epoll_create1(raw_flags: c_int) -> Result<isize> {
|
|
debug!("epoll_create: raw_flags: {:?}", raw_flags);
|
|
|
|
// Only O_CLOEXEC is valid
|
|
let flags = CreationFlags::from_bits(raw_flags as u32)
|
|
.ok_or_else(|| errno!(EINVAL, "invalid flags"))?
|
|
& CreationFlags::O_CLOEXEC;
|
|
let epoll_file: Arc<EpollFile> = EpollFile::new();
|
|
let close_on_spawn = flags.contains(CreationFlags::O_CLOEXEC);
|
|
let epfd = current!().add_file(epoll_file, close_on_spawn);
|
|
Ok(epfd as isize)
|
|
}
|
|
|
|
pub fn do_epoll_ctl(
|
|
epfd: c_int,
|
|
op: c_int,
|
|
fd: c_int,
|
|
event_ptr: *const libc::epoll_event,
|
|
) -> Result<isize> {
|
|
debug!("epoll_ctl: epfd: {}, op: {:?}, fd: {}", epfd, op, fd);
|
|
|
|
let get_c_event = |event_ptr| -> Result<&libc::epoll_event> {
|
|
from_user::check_ptr(event_ptr)?;
|
|
Ok(unsafe { &*event_ptr })
|
|
};
|
|
|
|
let fd = fd as FileDesc;
|
|
let ctl_cmd = match op {
|
|
libc::EPOLL_CTL_ADD => {
|
|
let c_event = get_c_event(event_ptr)?;
|
|
let event = EpollEvent::from_c(c_event);
|
|
let flags = EpollFlags::from_c(c_event);
|
|
EpollCtl::Add(fd, event, flags)
|
|
}
|
|
libc::EPOLL_CTL_DEL => EpollCtl::Del(fd),
|
|
libc::EPOLL_CTL_MOD => {
|
|
let c_event = get_c_event(event_ptr)?;
|
|
let event = EpollEvent::from_c(c_event);
|
|
let flags = EpollFlags::from_c(c_event);
|
|
EpollCtl::Mod(fd, event, flags)
|
|
}
|
|
_ => return_errno!(EINVAL, "invalid op"),
|
|
};
|
|
|
|
let epfile_ref = current!().file(epfd as FileDesc)?;
|
|
let epoll_file = epfile_ref.as_epoll_file()?;
|
|
|
|
epoll_file.control(&ctl_cmd)?;
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn do_epoll_wait(
|
|
epfd: c_int,
|
|
events: *mut libc::epoll_event,
|
|
max_events: c_int,
|
|
timeout_ms: c_int,
|
|
) -> Result<isize> {
|
|
debug!(
|
|
"epoll_wait: epfd: {}, max_events: {:?}, timeout_ms: {}",
|
|
epfd, max_events, timeout_ms
|
|
);
|
|
|
|
let max_events = {
|
|
if max_events <= 0 {
|
|
return_errno!(EINVAL, "maxevents <= 0");
|
|
}
|
|
max_events as usize
|
|
};
|
|
let raw_events = {
|
|
from_user::check_mut_array(events, max_events)?;
|
|
unsafe { std::slice::from_raw_parts_mut(events, max_events) }
|
|
};
|
|
|
|
// A new vector to store EpollEvent, which may degrade the performance due to extra copy.
|
|
let mut inner_events: Vec<MaybeUninit<EpollEvent>> = vec![MaybeUninit::uninit(); max_events];
|
|
|
|
debug!(
|
|
"epoll_wait: epfd: {}, len: {:?}, timeout: {}",
|
|
epfd,
|
|
raw_events.len(),
|
|
timeout_ms,
|
|
);
|
|
|
|
let epfile_ref = current!().file(epfd as FileDesc)?;
|
|
let epoll_file = epfile_ref.as_epoll_file()?;
|
|
let timeout = if timeout_ms >= 0 {
|
|
Some(Duration::from_millis(timeout_ms as u64))
|
|
} else {
|
|
None
|
|
};
|
|
let count = epoll_file.wait(&mut inner_events, timeout.as_ref())?;
|
|
|
|
for i in 0..count {
|
|
raw_events[i] = unsafe { inner_events[i].assume_init() }.to_c();
|
|
}
|
|
|
|
Ok(count as isize)
|
|
}
|
|
|
|
pub fn do_epoll_pwait(
|
|
epfd: c_int,
|
|
events: *mut libc::epoll_event,
|
|
maxevents: c_int,
|
|
timeout: c_int,
|
|
sigmask: *const usize, //TODO:add sigset_t
|
|
) -> Result<isize> {
|
|
if !sigmask.is_null() {
|
|
warn!("epoll_pwait cannot handle signal mask, yet");
|
|
} else {
|
|
debug!("epoll_wait");
|
|
}
|
|
do_epoll_wait(epfd, events, maxevents, timeout)
|
|
}
|