168 lines
5.5 KiB
Rust
168 lines
5.5 KiB
Rust
//! 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<Path>,
|
|
target: impl AsRef<Path>,
|
|
fstype: impl AsRef<str>,
|
|
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::<c_void>(),
|
|
)
|
|
} {
|
|
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<Path>, mode: impl AsRef<str>, 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::<i8>()) };
|
|
|
|
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<str>) -> 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<RawFd> {
|
|
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<usize> {
|
|
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<usize> {
|
|
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}"),
|
|
}
|
|
}
|