Create an init process in Rust that can mount the initial file systems appropriately.

This commit is contained in:
2021-01-06 08:33:28 -08:00
parent 1838e73db3
commit 5def920817
11 changed files with 205 additions and 36 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
old/ old/
tmp/ tmp/
artifacts/ artifacts/
target/
Cargo.lock

11
Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "lam"
version = "0.1.0"
authors = ["Adam Wick <awick@uhsure.com>"]
edition = "2018"
[dependencies]
libc = "0.2.81"
log = "0.4.11"
simple_logger = "1.11.0"
thiserror = "1.0.23"

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
# Introduction
The repository is an exploration of the question: what if we wanted to use
the Linux kernel as a poor man's microkernel? Microkernels are cool and all,
but figuring out how to deal with all the device drivers is a bit soul
crushing. So here, we're going to skip over those details, and see what it'd
look like if we just built everything else: the network stack, the file
systems, the graphical infrastructure, virtualization, etc. And let's use
Rust to do it, because I'm a fanboy. And let's not keep ourselves to
POSIX and standard Linux file system organization, because why bother?
# To Hit Go
Well, you'll need [Docker](https://www.docker.com/products/docker-desktop).
You'll need to use a fancy Rust Docker container or two. Run this:
```
% ./scripts/build-docker-envs.sh
```
To build your handy-dandy Docker containers.
To build the linux kernel, which you'll need, you should run:
```
% ./scripts/build-kernel.sh
```
You'll then want to run the Rust build:
```
% cargo build --release --target x86_65-unknown-linux-musl
```
And then build this all up into a nice ramdisk:
```
% ./scripts/build-ramfs.sh
```
And run the demo:
```
% ./scripts/run.sh
```
Wheee!! Fun!!

View File

@@ -20,5 +20,5 @@ RUN dnf install -y \
RUN mkdir -p /build/ramfs RUN mkdir -p /build/ramfs
RUN curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${KERNEL_VERSION}.tar.xz | tar xJC /build RUN curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${KERNEL_VERSION}.tar.xz | tar xJC /build
RUN mv /build/linux* /build/kernel RUN mv /build/linux* /build/kernel
COPY buildramfs.sh /build/ramfs/buildramfs.sh
COPY lam.config /build/kernel/.config COPY lam.config /build/kernel/.config
COPY buildramfs.sh /build/ramfs/buildramfs.sh

12
docker/Dockerfile.rust Normal file
View File

@@ -0,0 +1,12 @@
FROM fedora:latest
ENV USER=lam
ENV PASS=lampass
RUN useradd -m -G wheel -p ${PASS} ${USER}
RUN dnf install -y \
clang \
gcc
USER ${USER}
WORKDIR /home/${USER}
RUN mkdir -p /home/${USER}/lam
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable -t x86_64-unknown-linux-musl -y

View File

@@ -1,40 +1,12 @@
#!/bin/bash #!/bin/bash
ARCH="x86_64"
BB_VER="1.31.0"
# Dirs # Dirs
mkdir -p root mkdir -p image/{bin,dev,etc,lib,mnt,proc,sbin,sys,tmp,var}
cd root cd image
mkdir -p bin dev etc lib mnt proc sbin sys tmp var mkdir -p
cd - cd -
# Utils
if [ ! -f "root/bin/busybox" ]; then
curl -L "https://www.busybox.net/downloads/binaries/${BB_VER}-defconfig-multiarch-musl/busybox-${ARCH}" >root/bin/busybox
fi
cd root/bin
chmod +x busybox
ln -s busybox mount
ln -s busybox sh
cd -
# Init process
cat >>root/init << EOF
#!/bin/busybox sh
/bin/busybox --install -s /bin
mount -t devtmpfs devtmpfs /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /tmp
setsid cttyhack sh
exec /bin/sh
EOF
chmod +x root/init
# initramfs creation # initramfs creation
cd image
cd root
find . | cpio -ov --format=newc | gzip -9 >../initramfs find . | cpio -ov --format=newc | gzip -9 >../initramfs
cd - cd -

4
scripts/build-docker-envs.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
docker build -f docker/Dockerfile.rust -t lam-rust:latest docker
docker build -f docker/Dockerfile.kernel -t lam-kernel:latest docker

View File

@@ -1,4 +1,12 @@
#!/bin/sh #!/bin/sh
mkdir -p artifacts/ramdisk BASE=target/x86_64-unknown-linux-musl/release/
docker run --rm -v "$(pwd)/artifacts/ramdisk:/build/artifacts" lam-kernel bash -c "cd /build/ramfs && ./buildramfs.sh && cp initramfs /build/artifacts/initramfs.cpio.gz"
mkdir -p target/image
cp ${BASE}/init target/image/init
docker run --rm \
-v "$(pwd)/artifacts/ramdisk:/build/artifacts" \
-v "$(pwd)/target/image:/build/ramfs/image" \
lam-kernel \
bash -c "cd /build/ramfs && ./buildramfs.sh && cp initramfs /build/artifacts/initramfs.cpio.gz"

3
scripts/rust-build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
docker run -v $(pwd):/home/lam/build -w /home/lam/build lam-rust:latest bash -lc "cargo build --release --target x86_64-unknown-linux-musl"

3
scripts/rust-clippy.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
docker run -v $(pwd):/home/lam/build -w /home/lam/build lam-rust:latest bash -lc "cargo clippy --release --target x86_64-unknown-linux-musl"

106
src/bin/init.rs Normal file
View File

@@ -0,0 +1,106 @@
use log::{error, info};
use simple_logger::SimpleLogger;
use std::convert::Infallible;
use std::ffi::{CString, NulError};
use std::fmt;
use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
enum InitError {
IOError(#[from] io::Error),
NullReference(#[from] NulError),
TheUniverseBroke(#[from] Infallible),
MountError(libc::c_int, String),
}
impl fmt::Display for InitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InitError::IOError(e) => write!(f, "IO error: {}", e),
InitError::NullReference(ne) => write!(
f,
"This shouldn't happen, but encountered a null error: {}",
ne
),
InitError::TheUniverseBroke(e) => write!(f, "The world broke, somehow: {}", e),
InitError::MountError(e, s) => {
write!(f, "Failed to mount basic target {} with errno {}", s, e)
}
}
}
}
fn mount(
source: &str,
fs_type: &str,
target: &str,
flags: &str,
permissions: libc::c_ulong,
) -> Result<(), InitError> {
unsafe {
let source_c = CString::new(source)?;
let target_c = CString::new(target)?;
let fs_type_c = CString::new(fs_type)?;
let flags_c = CString::new(flags)?;
let res = libc::mount(
source_c.as_ptr(),
target_c.as_ptr(),
fs_type_c.as_ptr(),
permissions,
flags_c.as_ptr() as *const libc::c_void,
);
if res != 0 {
let errno = *(libc::__errno_location());
Err(InitError::MountError(errno, source.to_string()))
} else {
info!("Mounted {} on {}", fs_type, target);
Ok(())
}
}
}
fn real_main() -> Result<(), InitError> {
mount(
"proc",
"proc",
"/proc",
"hidepid=2",
libc::MS_NOSUID | libc::MS_NOEXEC | libc::MS_NODEV,
)?;
mount(
"devtmpfs",
"devtmpfs",
"/dev",
"mode=755",
libc::MS_NOSUID | libc::MS_STRICTATIME,
)?;
mount(
"tmpfs",
"tmpfs",
"/tmp",
"",
libc::MS_NOSUID | libc::MS_NODEV,
)?;
mount(
"sysfs",
"sysfs",
"/sys",
"",
libc::MS_NOSUID | libc::MS_NOEXEC | libc::MS_NODEV,
)?;
Ok(())
}
fn main() {
SimpleLogger::new().init().unwrap();
if let Err(e) = real_main() {
error!("Init exited with a top-level error: {}", e);
}
unsafe {
libc::reboot(libc::LINUX_REBOOT_CMD_POWER_OFF);
}
}