Compare commits
No commits in common. "main" and "refactor-enclaveos-to-nit" have entirely different histories.
main
...
refactor-e
|
@ -0,0 +1,16 @@
|
|||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "nit"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
|
||||
[features]
|
||||
aws = []
|
||||
default = ["aws"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.172"
|
|
@ -1,16 +0,0 @@
|
|||
FROM stagex/pallet-rust@sha256:740b9ed5f2a897d45cafdc806976d84231aa50a64998610750b42a48f8daacab as build
|
||||
|
||||
ENV TARGET=x86_64-unknown-linux-musl
|
||||
ENV RUSTFLAGS="-C target-feature=+crt-static"
|
||||
ENV CARGOFLAGS="--locked --no-default-features --release --target ${TARGET}"
|
||||
|
||||
ADD . /src
|
||||
|
||||
WORKDIR /src/init
|
||||
RUN cargo build ${CARGOFLAGS}
|
||||
|
||||
WORKDIR /rootfs
|
||||
RUN cp /src/init/target/${TARGET}/release/init .
|
||||
|
||||
FROM scratch as package
|
||||
COPY --from=build /rootfs .
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Distrust
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,5 +0,0 @@
|
|||
Copyright 2024 Distrust, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
13
Makefile
13
Makefile
|
@ -1,13 +0,0 @@
|
|||
.PHONY: default
|
||||
default: out/init
|
||||
|
||||
out:
|
||||
mkdir out
|
||||
|
||||
out/init: out
|
||||
docker build \
|
||||
--tag local/init \
|
||||
--progress=plain \
|
||||
--output type=local,rewrite-timestamp=true,dest=out\
|
||||
-f Containerfile \
|
||||
src/
|
|
@ -1,24 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aws"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
|
@ -1,10 +0,0 @@
|
|||
[package]
|
||||
name = "aws"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.134"
|
||||
system = { path = "../system"}
|
|
@ -1,34 +0,0 @@
|
|||
use system::{dmesg, SystemError};
|
||||
|
||||
// Signal to Nitro hypervisor that booting was successful
|
||||
fn nitro_heartbeat() {
|
||||
use system::socket_connect;
|
||||
use libc::{write, read, close, AF_VSOCK};
|
||||
let mut buf: [u8; 1] = [0; 1];
|
||||
buf[0] = 0xB7; // AWS Nitro heartbeat value
|
||||
let fd = match socket_connect(AF_VSOCK, 9000, 3) {
|
||||
Ok(f)=> f,
|
||||
Err(e)=> {
|
||||
eprintln!("{}", e);
|
||||
return
|
||||
},
|
||||
};
|
||||
unsafe {
|
||||
write(fd, buf.as_ptr() as _, 1);
|
||||
read(fd, buf.as_ptr() as _, 1);
|
||||
close(fd);
|
||||
}
|
||||
dmesg(format!("Sent NSM heartbeat"));
|
||||
}
|
||||
|
||||
// Initialize nitro device
|
||||
pub fn init_platform(){
|
||||
use system::insmod;
|
||||
// TODO: error handling
|
||||
nitro_heartbeat();
|
||||
|
||||
match insmod("/nsm.ko") {
|
||||
Ok(())=> dmesg(format!("Loaded nsm.ko")),
|
||||
Err(e)=> eprintln!("{}", e)
|
||||
};
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aws"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "init"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aws",
|
||||
"libc",
|
||||
"system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
|
||||
[[package]]
|
||||
name = "system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "init"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.134"
|
||||
aws = { path = "../aws"}
|
||||
system = { path = "../system"}
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "init"
|
||||
path = "init.rs"
|
|
@ -1,54 +0,0 @@
|
|||
use system::{reboot, freopen, mount, dmesg};
|
||||
|
||||
use aws::{init_platform};
|
||||
|
||||
// Mount common filesystems with conservative permissions
|
||||
fn init_rootfs() {
|
||||
use libc::{MS_NOSUID, MS_NOEXEC, MS_NODEV };
|
||||
let no_dse = MS_NODEV | MS_NOSUID | MS_NOEXEC;
|
||||
let no_se = MS_NOSUID | MS_NOEXEC;
|
||||
let args = [
|
||||
("devtmpfs", "/dev", "devtmpfs", no_se, "mode=0755"),
|
||||
("devtmpfs", "/dev", "devtmpfs", no_se, "mode=0755"),
|
||||
("devpts", "/dev/pts", "devpts", no_se, ""),
|
||||
("shm", "/dev/shm", "tmpfs", no_dse, "mode=0755"),
|
||||
("proc", "/proc", "proc", no_dse, "hidepid=2"),
|
||||
("tmpfs", "/run", "tmpfs", no_dse, "mode=0755"),
|
||||
("tmpfs", "/tmp", "tmpfs", no_dse, ""),
|
||||
("sysfs", "/sys", "sysfs", no_dse, ""),
|
||||
("cgroup_root", "/sys/fs/cgroup", "tmpfs", no_dse, "mode=0755"),
|
||||
];
|
||||
for (src, target, fstype, flags, data) in args {
|
||||
match mount(src, target, fstype, flags, data) {
|
||||
Ok(())=> dmesg(format!("Mounted {}", target)),
|
||||
Err(e)=> eprintln!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize console with stdin/stdout/stderr
|
||||
fn init_console() {
|
||||
let args = [
|
||||
("/dev/console", "r", 0),
|
||||
("/dev/console", "w", 1),
|
||||
("/dev/console", "w", 2),
|
||||
];
|
||||
for (filename, mode, file) in args {
|
||||
match freopen(filename, mode, file) {
|
||||
Ok(())=> {},
|
||||
Err(e)=> eprintln!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn boot(){
|
||||
init_rootfs();
|
||||
init_console();
|
||||
init_platform();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
boot();
|
||||
dmesg("System booted".to_string());
|
||||
reboot();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
mod result;
|
||||
mod system;
|
||||
mod platform;
|
||||
|
||||
use result::Result;
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = init() {
|
||||
eprintln!("Error: {e}");
|
||||
let mut opt = Some(&e as &dyn std::error::Error);
|
||||
while let Some(current_source) = opt {
|
||||
eprintln!("Caused by: {current_source}");
|
||||
opt = current_source.source();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init() -> Result<()> {
|
||||
platform::init()?;
|
||||
println!("Hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
use crate::result::{Result, Context};
|
||||
|
||||
pub struct Aws;
|
||||
|
||||
impl Aws {
|
||||
fn init_heartbeat() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Platform for Aws {
|
||||
fn is(&self) -> Result<bool> {
|
||||
std::fs::exists("/dev/nsm").context(format_args!("could not check if /dev/nsm exists"))
|
||||
}
|
||||
|
||||
fn get_modules(&self) -> Result<Vec<(String, String)>> {
|
||||
Ok(vec![
|
||||
("/nsm.ko".into(), String::new())
|
||||
])
|
||||
}
|
||||
|
||||
fn init(&self) -> Result<()> {
|
||||
Self::init_heartbeat()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
use crate::{system::{self, Mount, MountType}, result::Result};
|
||||
|
||||
trait Platform {
|
||||
/// Whether the current Platform is the `Self` platform.
|
||||
///
|
||||
/// This method should check for the existence of hardware devices that irrefutably defines
|
||||
/// this platform as the given platform. For instance, the existence of `/dev/n`
|
||||
fn is(&self) -> Result<bool>;
|
||||
|
||||
/// Return the names and parameters for any required kernel modules.
|
||||
fn get_modules(&self) -> Result<Vec<(String, String)>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// The configuration for mounting filesystems for the platform.
|
||||
///
|
||||
/// This normally includes filesystems such as `/dev` and `/proc` that are not
|
||||
/// backed by physical media.
|
||||
fn get_mounts(&self) -> Result<Vec<Mount>> {
|
||||
use libc::{MS_NODEV, MS_NOEXEC, MS_NOSUID};
|
||||
use MountType::{DevPts, DevTmpFs, Proc, Shm, SysFs, TmpFs};
|
||||
|
||||
let no_se = MS_NOSUID | MS_NOEXEC;
|
||||
let no_dse = no_se | MS_NODEV;
|
||||
|
||||
let m755 = Some("mode=0755");
|
||||
|
||||
Ok(vec![
|
||||
Mount::new(DevTmpFs, "/dev", DevTmpFs, no_se, m755),
|
||||
Mount::new(DevPts, "/dev/pts", DevPts, no_se, None),
|
||||
Mount::new(Shm, "/dev/shm", TmpFs, no_dse, m755),
|
||||
Mount::new(Proc, "/proc", Proc, no_dse, m755),
|
||||
Mount::new(TmpFs, "/tmp", TmpFs, no_dse, None),
|
||||
Mount::new(SysFs, "/sys", SysFs, no_dse, None),
|
||||
Mount::new("cgroup_root", "/sys/fs/cgroup", TmpFs, no_dse, m755),
|
||||
])
|
||||
}
|
||||
|
||||
/// Initialize all necessary requirements for the platform.
|
||||
fn init(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn init_filesystems(iter: impl IntoIterator<Item = Mount>) -> Result<()> {
|
||||
for mount in iter {
|
||||
mount.mount_self()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_modules(iter: impl IntoIterator<Item = (String, String)>) -> Result<()> {
|
||||
for (module, params) in iter {
|
||||
system::insmod(&module, ¶ms)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws")]
|
||||
mod aws;
|
||||
|
||||
fn get_current_platform() -> Result<Option<Box<dyn Platform>>> {
|
||||
#[cfg(feature = "aws")]
|
||||
if aws::Aws.is()? {
|
||||
return Ok(Some(Box::new(aws::Aws)))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
// Error handling strategy: If a platform is compiled in and loaded, if platform
|
||||
// specific error handling doesn't work, fall back to generic.
|
||||
|
||||
let platform = get_current_platform()?;
|
||||
if let Some(platform) = platform {
|
||||
// NOTE: We need to make get_mounts _additional_ beyond a base set.
|
||||
// We need `/dev/nsm` to exist so Aws.is() works.
|
||||
platform.get_mounts().and_then(init_filesystems)?;
|
||||
platform.get_modules().and_then(init_modules)?;
|
||||
platform.init()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//! Error handling niceties.
|
||||
|
||||
// Because who needs anyhow anyways.
|
||||
|
||||
/// Provide context for an existing error.
|
||||
pub trait Context<T> {
|
||||
fn context(self, fmt: std::fmt::Arguments<'_>) -> Result<T, CtxError>;
|
||||
}
|
||||
|
||||
impl<T, E> Context<T> for std::result::Result<T, E>
|
||||
where
|
||||
E: std::error::Error + 'static,
|
||||
{
|
||||
fn context(self, fmt: std::fmt::Arguments<'_>) -> Result<T, CtxError> {
|
||||
match self {
|
||||
Ok(o) => Ok(o),
|
||||
Err(e) => Err(CtxError {
|
||||
context: fmt.to_string(),
|
||||
inner: Box::new(e),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error with context attached.
|
||||
///
|
||||
/// The context is provided as format arguments, but will be constructed into a string if the value
|
||||
/// is an error.
|
||||
#[derive(Debug)]
|
||||
pub struct CtxError {
|
||||
context: String,
|
||||
inner: Box<dyn std::error::Error>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CtxError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.context.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CtxError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
Some(self.inner.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T, E = CtxError> = std::result::Result<T, E>;
|
||||
|
||||
pub fn ctx_os_error(args: std::fmt::Arguments<'_>) -> Result<(), CtxError> {
|
||||
Err(std::io::Error::last_os_error()).context(args)
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
|
@ -1,9 +0,0 @@
|
|||
[package]
|
||||
name = "system"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.134"
|
|
@ -0,0 +1,85 @@
|
|||
use crate::result::{Result, Context};
|
||||
use std::path::{PathBuf, Path};
|
||||
|
||||
pub mod syscall;
|
||||
|
||||
pub enum MountType {
|
||||
DevTmpFs,
|
||||
DevPts,
|
||||
Shm,
|
||||
Proc,
|
||||
TmpFs,
|
||||
SysFs,
|
||||
}
|
||||
|
||||
impl From<MountType> for PathBuf {
|
||||
fn from(value: MountType) -> Self {
|
||||
Self::from(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl MountType {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
MountType::DevTmpFs => "devtmpfs",
|
||||
MountType::DevPts => "devpts",
|
||||
MountType::Shm => "shm",
|
||||
MountType::Proc => "proc",
|
||||
MountType::TmpFs => "tmpfs",
|
||||
MountType::SysFs => "sysfs",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mount {
|
||||
source: PathBuf,
|
||||
target: PathBuf,
|
||||
fstype: MountType,
|
||||
flags: libc::c_ulong,
|
||||
data: Option<String>,
|
||||
}
|
||||
|
||||
impl Mount {
|
||||
pub fn new<'a>(
|
||||
source: impl Into<PathBuf>,
|
||||
target: impl Into<PathBuf>,
|
||||
fstype: MountType,
|
||||
flags: libc::c_ulong,
|
||||
data: impl Into<Option<&'a str>>,
|
||||
) -> Self {
|
||||
let source = source.into();
|
||||
let target = target.into();
|
||||
let data = data.into().map(Into::into);
|
||||
|
||||
Self {
|
||||
source,
|
||||
target,
|
||||
fstype,
|
||||
flags,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mount_self(&self) -> Result<()> {
|
||||
syscall::mount(
|
||||
&self.source,
|
||||
&self.target,
|
||||
self.fstype.as_str(),
|
||||
self.flags,
|
||||
self.data.as_deref(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insmod(path: impl AsRef<Path>, params: impl AsRef<str>) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
let params = params.as_ref();
|
||||
let file = std::fs::File::open(path).context(format_args!(
|
||||
"could not open: {path}",
|
||||
path = path.display()
|
||||
))?;
|
||||
|
||||
syscall::finit_module(file, params)?;
|
||||
Ok(())
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
use libc::{ c_ulong, c_int, c_void };
|
||||
use std::{
|
||||
mem::{zeroed, size_of},
|
||||
ffi::CString,
|
||||
fs::File,
|
||||
os::unix::io::AsRawFd,
|
||||
fmt,
|
||||
};
|
||||
|
||||
pub struct SystemError {
|
||||
pub message: String,
|
||||
}
|
||||
impl fmt::Display for SystemError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", boot_time(), self.message)
|
||||
}
|
||||
}
|
||||
|
||||
// Log dmesg formatted log to console
|
||||
pub fn dmesg(message: String){
|
||||
println!("{} {}", boot_time(), message);
|
||||
}
|
||||
|
||||
// Dmesg formatted seconds since boot
|
||||
pub fn boot_time() -> String {
|
||||
use libc::{clock_gettime, timespec, CLOCK_BOOTTIME};
|
||||
let mut t = timespec { tv_sec: 0, tv_nsec: 0 };
|
||||
unsafe { clock_gettime(CLOCK_BOOTTIME, &mut t as *mut timespec); }
|
||||
format!("[ {: >4}.{}]", t.tv_sec, t.tv_nsec / 1000).to_string()
|
||||
}
|
||||
|
||||
// Unconditionally reboot the system now
|
||||
pub fn reboot(){
|
||||
use libc::{reboot, RB_AUTOBOOT};
|
||||
unsafe {
|
||||
reboot(RB_AUTOBOOT);
|
||||
}
|
||||
}
|
||||
|
||||
// libc::mount casting/error wrapper
|
||||
pub fn mount(
|
||||
src: &str,
|
||||
target: &str,
|
||||
fstype: &str,
|
||||
flags: c_ulong,
|
||||
data: &str,
|
||||
) -> Result<(), SystemError> {
|
||||
use libc::mount;
|
||||
let src_cs = CString::new(src).unwrap();
|
||||
let fstype_cs = CString::new(fstype).unwrap();
|
||||
let data_cs = CString::new(data).unwrap();
|
||||
let target_cs = CString::new(target).unwrap();
|
||||
if unsafe {
|
||||
mount(
|
||||
src_cs.as_ptr(),
|
||||
target_cs.as_ptr(),
|
||||
fstype_cs.as_ptr(),
|
||||
flags,
|
||||
data_cs.as_ptr() as *const c_void
|
||||
)
|
||||
} != 0 {
|
||||
Err(SystemError { message: format!("Failed to mount: {}", target) })
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// libc::freopen casting/error wrapper
|
||||
pub fn freopen(
|
||||
filename: &str,
|
||||
mode: &str,
|
||||
file: c_int,
|
||||
) -> Result<(), SystemError> {
|
||||
use libc::{freopen, fdopen};
|
||||
let filename_cs = CString::new(filename).unwrap();
|
||||
let mode_cs = CString::new(mode).unwrap();
|
||||
if unsafe {
|
||||
freopen(
|
||||
filename_cs.as_ptr(),
|
||||
mode_cs.as_ptr(),
|
||||
fdopen(file, mode_cs.as_ptr() as *const i8)
|
||||
)
|
||||
}.is_null() {
|
||||
Err(SystemError { message: format!("Failed to freopen: {}", filename) })
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Insert kernel module into memory
|
||||
pub fn insmod(path: &str) -> Result<(), SystemError> {
|
||||
use libc::{syscall, SYS_finit_module};
|
||||
let file = File::open(path).unwrap();
|
||||
let fd = file.as_raw_fd();
|
||||
if unsafe { syscall(SYS_finit_module, fd, &[0u8; 1], 0) } < 0 {
|
||||
Err(SystemError {
|
||||
message: format!("Failed to insert kernel module: {}", path)
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate a socket
|
||||
pub fn socket_connect(
|
||||
family: c_int,
|
||||
port: u32,
|
||||
cid: u32,
|
||||
) -> Result<c_int, SystemError> {
|
||||
use libc::{connect, socket, sockaddr, sockaddr_vm, SOCK_STREAM};
|
||||
let fd = unsafe { socket(family, SOCK_STREAM, 0) };
|
||||
if unsafe {
|
||||
let mut sa: sockaddr_vm = zeroed();
|
||||
sa.svm_family = family as _;
|
||||
sa.svm_port = port;
|
||||
sa.svm_cid = cid;
|
||||
connect(
|
||||
fd,
|
||||
&sa as *const _ as *mut sockaddr,
|
||||
size_of::<sockaddr_vm>() as _,
|
||||
)
|
||||
} < 0 {
|
||||
Err(SystemError {
|
||||
message: format!("Failed to connect to socket: {}", family)
|
||||
})
|
||||
} else {
|
||||
Ok(fd)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
//! 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,
|
||||
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}"),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue