//! Wrappers for libc system calls and other necessary functions. //! //! These functions, for one reason or another, require using the `libc` crate and can't //! be implemented directly in the standard library. // Some of these methods might not be called, because support for the features that // invoke the functions were not enabled. #![allow(dead_code)] use crate::result::{ctx_os_error, Context, Result}; use libc::{self, c_ulong, c_void}; use std::{ ffi::CString, os::fd::{AsRawFd, RawFd}, path::Path, }; pub fn mount( src: impl AsRef, target: impl AsRef, fstype: impl AsRef, flags: c_ulong, data: Option<&str>, ) -> Result<()> { let src = src.as_ref(); let target = target.as_ref(); let fstype = fstype.as_ref(); // it upsets me to no end that these two lines format differently. #[rustfmt::ignore] let src_cstr = CString::new(src.as_os_str().as_encoded_bytes()) .context(format_args!("bad src: {src}", src = src.display()))?; #[rustfmt::ignore] let target_cstr = CString::new(target.as_os_str().as_encoded_bytes()).context(format_args!( "bad target: {target}", target = target.display() ))?; let fstype_cs = CString::new(fstype).context(format_args!("bad fstype: {fstype}"))?; let data_ptr = if let Some(s) = data { s.as_ptr() } else { std::ptr::null() }; // SAFETY: mount() can accept nullptr for data. no other pointers are constructed manually. match unsafe { libc::mount( src_cstr.as_ptr(), target_cstr.as_ptr(), fstype_cs.as_ptr(), flags, data_ptr.cast::(), ) } { 0 => Ok(()), -1 => ctx_os_error(format_args!( "error calling mount({src}, {target}, {fstype}, {flags}, {data:?})", src = src.display(), target = target.display() )), n => unreachable!("mount() syscall returned bad value: {n}"), } } pub fn freopen(path: impl AsRef, mode: impl AsRef, fd: &impl AsRawFd) -> Result<()> { let path = path.as_ref(); let mode = mode.as_ref(); let fd = fd.as_raw_fd(); #[rustfmt::ignore] let filename_cs = CString::new(path.as_os_str().as_encoded_bytes()) .context(format_args!("bad path: {path}", path = path.display()))?; let mode_cs = CString::new(mode).context(format_args!("bad mode: {mode}"))?; // SAFETY: no pointers are constructed manually. let file = unsafe { libc::fdopen(fd, mode.as_ptr().cast::()) }; if file.is_null() { return ctx_os_error(format_args!("bad fdopen({fd}, {mode})")); } // SAFETY: no pointers are constructed manually. let file = unsafe { libc::freopen(filename_cs.as_ptr(), mode_cs.as_ptr(), file) }; if file.is_null() { return ctx_os_error(format_args!( "bad freopen({path}, {mode}, {file:?} as *FILE)", path = path.display() )); } Ok(()) } // NOTE: We want to consume the file, so we do not allow a ref to impl AsRawFd #[allow(clippy::needless_pass_by_value)] pub fn finit_module(fd: impl AsRawFd, params: impl AsRef) -> Result<()> { let fd = fd.as_raw_fd(); let params = params.as_ref(); let params_cs = CString::new(params).context(format_args!("bad params: {params}"))?; // SAFETY: no pointers are constructed manually, and AsRawFd should be a proper // file descriptor. match unsafe { libc::syscall(libc::SYS_finit_module, fd, params_cs.as_ptr(), 0) } { 0 => Ok(()), -1 => ctx_os_error(format_args!("error calling finit_module({fd}, {params})")), n => unreachable!("syscall(SYS_finit_module, ...) returned bad value: {n}"), } } #[repr(i32)] #[derive(Debug, Clone, Copy)] pub enum SocketFamily { Vsock = libc::AF_VSOCK, } #[repr(i32)] #[derive(Debug, Clone, Copy)] pub enum SocketType { Stream = libc::SOCK_STREAM, } // TODO: allow specifying protocol? pub fn socket(family: SocketFamily, typ: SocketType) -> Result { match unsafe { libc::socket(family as i32, typ as i32, 0) } { -1 => ctx_os_error(format_args!("error calling socket({family:?}, {typ:?})")).map(|()| 0), fd => Ok(RawFd::from(fd)), } } pub use libc::sockaddr_vm; // This function is unsafe since we have to pass it a C-style union. pub unsafe fn connect(fd: RawFd, sockaddr: *mut libc::sockaddr, size: usize) -> Result<()> { let size = u32::try_from(size).context(format_args!( "connect(..., size = {size}) has size > {}", u32::MAX ))?; match unsafe { libc::connect(fd, sockaddr, size) } { 0 => Ok(()), -1 => ctx_os_error(format_args!("error calling connect({fd}, ...)")), n => unreachable!("connect({fd}, ...) returned bad value: {n}"), } } pub fn write(fd: RawFd, bytes: &[u8]) -> Result { match unsafe { libc::write(fd, bytes.as_ptr().cast(), bytes.len()) } { ..0 => ctx_os_error(format_args!("error calling write({fd}, ...)")).map(|()| 0), n @ 0.. => Ok(usize::try_from(n).unwrap()), } } pub fn read(fd: RawFd, buffer: &mut [u8]) -> Result { match unsafe { libc::read(fd, buffer.as_mut_ptr().cast(), buffer.len()) } { ..0 => ctx_os_error(format_args!("error calling read({fd}, ...)")).map(|()| 0), n @ 0.. => Ok(usize::try_from(n).unwrap()), } } pub fn close(fd: RawFd) -> Result<()> { match unsafe { libc::close(fd) } { 0 => Ok(()), -1 => ctx_os_error(format_args!("error calling close({fd})")), n => unreachable!("close({fd}) returned bad value: {n}"), } }