코드정리, kmsg 기반으로 로그 전송하도록 처리, allocator 메모리 재활용하도록 처리, init 찾을 수 있도록 처리
This commit is contained in:
parent
a249d59b63
commit
af8bbab8bf
|
@ -10,7 +10,7 @@ LABEL org.opencontainers.image.authors="Sangbum Kim <sangbumkim@amuz.es>"
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
|
||||||
ENV RUSTFLAGS='-C link-arg=-s -C link-arg=-fuse-ld=lld'
|
ENV RUSTFLAGS='-C panic=abort -C link-arg=-s -C link-arg=-fuse-ld=lld'
|
||||||
|
|
||||||
RUN set -x && \
|
RUN set -x && \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
|
@ -22,6 +22,7 @@ RUN set -x && \
|
||||||
RUN set -x && \
|
RUN set -x && \
|
||||||
cargo build --release && \
|
cargo build --release && \
|
||||||
ls -alh target/release/init-wrapper
|
ls -alh target/release/init-wrapper
|
||||||
|
# ldd target/release/init-wrapper && \
|
||||||
# && \
|
# && \
|
||||||
# ldd target/release/init-wrapper
|
# ldd target/release/init-wrapper
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ mount -t overlay -o rw,relatime,lowerdir=/run/overlay/lower,upperdir=/run/overla
|
||||||
mkdir -p /run/overlay/merged/oldroot
|
mkdir -p /run/overlay/merged/oldroot
|
||||||
pivot_root /run/overlay/merged /run/overlay/merged/oldroot
|
pivot_root /run/overlay/merged /run/overlay/merged/oldroot
|
||||||
cd /
|
cd /
|
||||||
|
mount --move /oldroot/dev /dev || true
|
||||||
mount --move /oldroot/run /run
|
mount --move /oldroot/run /run
|
||||||
umount -l /oldroot
|
umount -l /oldroot
|
||||||
rmdir /oldroot
|
rmdir /oldroot
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
use alloc::format;
|
||||||
|
use core::ffi::CStr;
|
||||||
|
use libc::{c_char, c_int, open, FILE, O_APPEND, O_CLOEXEC, O_SYNC, O_WRONLY, STDERR_FILENO};
|
||||||
|
use libc_print::__LibCWriter;
|
||||||
|
pub(crate) static mut LOG_FD: c_int = STDERR_FILENO;
|
||||||
|
pub(crate) static mut LOG_FD_KMSG: bool = false;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn init_kmsg() {
|
||||||
|
const DEV_KMSG_PATH: *const c_char =
|
||||||
|
unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kmsg\0").as_ptr() };
|
||||||
|
const LOG_FLAGS: c_int = O_APPEND | O_WRONLY | O_CLOEXEC | O_SYNC;
|
||||||
|
|
||||||
|
let fd = unsafe { open(DEV_KMSG_PATH, LOG_FLAGS) };
|
||||||
|
match fd {
|
||||||
|
-1 => (),
|
||||||
|
fd => unsafe {
|
||||||
|
crate::kmsg::LOG_FD = fd;
|
||||||
|
LOG_FD_KMSG = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! kprintln {
|
||||||
|
() => { $crate::kprintln!("") };
|
||||||
|
($fmt: tt) => {
|
||||||
|
{
|
||||||
|
#[allow(unused_must_use)]
|
||||||
|
unsafe {
|
||||||
|
let mut stm = libc_print::__LibCWriter::new($crate::kmsg::LOG_FD);
|
||||||
|
stm.write_str(concat!("init-wrapper: ",$fmt));
|
||||||
|
if (!$crate::kmsg::LOG_FD_KMSG) {
|
||||||
|
stm.write_nl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($fmt: tt, $($arg:tt)*) =>{
|
||||||
|
{
|
||||||
|
|
||||||
|
#[allow(unused_must_use)]
|
||||||
|
unsafe {
|
||||||
|
let mut stm = libc_print::__LibCWriter::new($crate::kmsg::LOG_FD);
|
||||||
|
if ($crate::kmsg::LOG_FD_KMSG) {
|
||||||
|
let buf=alloc::format!(concat!("init-wrapper: ",$fmt),$($arg)*);
|
||||||
|
for line in buf.lines(){
|
||||||
|
stm.write_str(&line);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
stm.write_fmt(format_args!(concat!("init-wrapper: ",$fmt),$($arg)*));
|
||||||
|
stm.write_nl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
11
src/link.rs
11
src/link.rs
|
@ -1,6 +1,6 @@
|
||||||
#![allow(non_camel_case_types, dead_code)]
|
#![allow(non_camel_case_types, dead_code)]
|
||||||
|
|
||||||
use libc::FILE;
|
use libc::{exit, FILE};
|
||||||
|
|
||||||
#[link(name = "c")]
|
#[link(name = "c")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -9,9 +9,16 @@ extern "C" {
|
||||||
static stderr: *mut FILE;
|
static stderr: *mut FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn rust_eh_personality() {}
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn _Unwind_Resume() -> ! {
|
||||||
|
exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
unsafe fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
unsafe fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||||
libc::exit(2)
|
exit(2)
|
||||||
}
|
}
|
||||||
|
|
258
src/main.rs
258
src/main.rs
|
@ -2,167 +2,30 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
mod kmsg;
|
||||||
mod link;
|
mod link;
|
||||||
mod mem;
|
mod mem;
|
||||||
|
mod tm;
|
||||||
|
mod unix;
|
||||||
|
use errno::Errno;
|
||||||
|
|
||||||
use alloc::ffi::CString;
|
use kmsg::init_kmsg;
|
||||||
use core::ffi::CStr;
|
|
||||||
use core::ops::Sub;
|
|
||||||
use core::ptr::null;
|
|
||||||
use core::time::Duration;
|
|
||||||
use errno::{errno, Errno};
|
|
||||||
use libc::{
|
use libc::{
|
||||||
c_int, c_void, chdir, clock_gettime, execv, mkdir, mode_t, mount, rmdir, syscall, timespec,
|
c_int, EACCES, EINVAL, ENOENT, ENOEXEC, ENOTDIR, EXIT_FAILURE, EXIT_SUCCESS, MNT_DETACH,
|
||||||
umount2, SYS_pivot_root, CLOCK_BOOTTIME, EXIT_FAILURE, EXIT_SUCCESS, MNT_DETACH, MS_BIND,
|
MS_BIND, MS_LAZYTIME, MS_MOVE, MS_NODEV, MS_NOSUID, MS_PRIVATE, MS_RDONLY, MS_REC, MS_RELATIME,
|
||||||
MS_LAZYTIME, MS_MOVE, MS_NODEV, MS_NOSUID, MS_PRIVATE, MS_RDONLY, MS_REC, MS_RELATIME,
|
|
||||||
};
|
};
|
||||||
use libc_print::std_name::eprintln;
|
|
||||||
|
|
||||||
struct Timespec {
|
use unix::{
|
||||||
ts: timespec,
|
do_chdir, do_execv, do_gettime, do_mkdir, do_mount, do_pivot_root, do_rmdir, do_umount,
|
||||||
}
|
};
|
||||||
|
|
||||||
type SystemResult = Result<(), Errno>;
|
|
||||||
|
|
||||||
impl Sub<Timespec> for Timespec {
|
|
||||||
type Output = Duration;
|
|
||||||
|
|
||||||
fn sub(self, other: Timespec) -> Duration {
|
|
||||||
let sec = self.ts.tv_sec - other.ts.tv_sec;
|
|
||||||
let nsec = self.ts.tv_nsec - other.ts.tv_nsec;
|
|
||||||
if nsec < 0 {
|
|
||||||
Duration::from_secs(sec as u64) - Duration::from_nanos(nsec.unsigned_abs())
|
|
||||||
} else {
|
|
||||||
Duration::from_secs(sec as u64) + Duration::from_nanos(nsec.unsigned_abs())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<timespec> for Timespec {
|
|
||||||
fn from(item: timespec) -> Self {
|
|
||||||
Timespec { ts: item }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<timespec> for Timespec {
|
|
||||||
fn into(self) -> timespec {
|
|
||||||
self.ts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_mount(
|
|
||||||
source: Option<&str>,
|
|
||||||
target: Option<&str>,
|
|
||||||
fs: Option<&str>,
|
|
||||||
flags: u64,
|
|
||||||
opt: Option<&str>,
|
|
||||||
) -> SystemResult {
|
|
||||||
// has ownership
|
|
||||||
let raw_src = source.map(|v| CString::new(v).ok()).flatten();
|
|
||||||
let raw_tgt = target.map(|v| CString::new(v).ok()).flatten();
|
|
||||||
let raw_fs = fs.map(|v| CString::new(v).ok()).flatten();
|
|
||||||
let raw_fs_opt = opt.map(|v| CString::new(v).ok()).flatten();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if mount(
|
|
||||||
raw_src.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
|
||||||
raw_tgt.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
|
||||||
raw_fs.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
|
||||||
flags,
|
|
||||||
raw_fs_opt.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null) as *const c_void,
|
|
||||||
) == -1
|
|
||||||
{
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn do_umount(path: &str, flags: i32) -> SystemResult {
|
|
||||||
// has ownership
|
|
||||||
let raw_path = CString::new(path).unwrap();
|
|
||||||
unsafe {
|
|
||||||
if umount2(raw_path.as_ref().as_ptr(), flags) == -1 {
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_mkdir(path: &str, mode: mode_t) -> SystemResult {
|
|
||||||
// has ownership
|
|
||||||
let raw_path = CString::new(path).unwrap();
|
|
||||||
unsafe {
|
|
||||||
if mkdir(raw_path.as_ref().as_ptr(), mode) == -1 {
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn do_rmdir(path: &str) -> SystemResult {
|
|
||||||
// has ownership
|
|
||||||
let raw_path = CString::new(path).unwrap();
|
|
||||||
unsafe {
|
|
||||||
if rmdir(raw_path.as_ref().as_ptr()) == -1 {
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn do_chdir(path: &str) -> SystemResult {
|
|
||||||
// has ownership
|
|
||||||
let raw_path = CString::new(path).unwrap();
|
|
||||||
unsafe {
|
|
||||||
if chdir(raw_path.as_ref().as_ptr()) == -1 {
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pivot_root(new_root: &str, put_old: &str) -> SystemResult {
|
|
||||||
let raw_new_root = CString::new(new_root).unwrap();
|
|
||||||
let raw_put_old = CString::new(put_old).unwrap();
|
|
||||||
unsafe {
|
|
||||||
if syscall(
|
|
||||||
SYS_pivot_root,
|
|
||||||
raw_new_root.as_ref().as_ptr(),
|
|
||||||
raw_put_old.as_ref().as_ptr(),
|
|
||||||
) == -1
|
|
||||||
{
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_gettime() -> Result<Timespec, Errno> {
|
|
||||||
let mut time = timespec {
|
|
||||||
tv_sec: 0,
|
|
||||||
tv_nsec: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if clock_gettime(CLOCK_BOOTTIME, &mut time) == -1 {
|
|
||||||
Err(errno())
|
|
||||||
} else {
|
|
||||||
Ok(time.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_root() -> Result<(), ()> {
|
fn replace_root() -> Result<(), ()> {
|
||||||
match do_mount(None, Some("/"), None, MS_REC | MS_PRIVATE, None) {
|
match do_mount(None, Some("/"), None, MS_REC | MS_PRIVATE, None) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("remount \"/\" with private option failed: {}", e);
|
kprintln!("remount \"/\" with private option failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"/\" remounted"),
|
Ok(_) => kprintln!("\"/\" remounted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match do_mount(
|
match do_mount(
|
||||||
|
@ -173,10 +36,10 @@ fn replace_root() -> Result<(), ()> {
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("mount \"/run\" failed: {}", e);
|
kprintln!("mount \"/run\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"/run\" mounted"),
|
Ok(_) => kprintln!("\"/run\" mounted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ovr_dir_structures = [
|
let ovr_dir_structures = [
|
||||||
|
@ -186,13 +49,14 @@ fn replace_root() -> Result<(), ()> {
|
||||||
("/run/overlay/work", 0o0755),
|
("/run/overlay/work", 0o0755),
|
||||||
("/run/overlay/merged", 0o0755),
|
("/run/overlay/merged", 0o0755),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (path, mode) in ovr_dir_structures {
|
for (path, mode) in ovr_dir_structures {
|
||||||
match do_mkdir(path, mode) {
|
match do_mkdir(path, mode) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("mkdir \"{}\" failed: {}", path, e);
|
kprintln!("mkdir \"{}\" failed: {}", path, e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"{}\" created", path),
|
Ok(_) => kprintln!("\"{}\" created", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,10 +68,10 @@ fn replace_root() -> Result<(), ()> {
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("mount \"/run/overlay/lower\" failed: {}", e);
|
kprintln!("mount \"/run/overlay/lower\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"/run/overlay/lower\" mounted"),
|
Ok(_) => kprintln!("\"/run/overlay/lower\" mounted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match do_mount(
|
match do_mount(
|
||||||
|
@ -216,66 +80,70 @@ fn replace_root() -> Result<(), ()> {
|
||||||
Some("overlay"), MS_RELATIME|MS_LAZYTIME,
|
Some("overlay"), MS_RELATIME|MS_LAZYTIME,
|
||||||
Some("lowerdir=/run/overlay/lower,upperdir=/run/overlay/upper,workdir=/run/overlay/work,redirect_dir=on,uuid=on,metacopy=on,volatile"),
|
Some("lowerdir=/run/overlay/lower,upperdir=/run/overlay/upper,workdir=/run/overlay/work,redirect_dir=on,uuid=on,metacopy=on,volatile"),
|
||||||
) {
|
) {
|
||||||
Err(e) => {eprintln!("mount \"/run/overlay/merged\" failed: {}", e);
|
Err(e) => {kprintln!("mount \"/run/overlay/merged\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
},
|
},
|
||||||
Ok(_) => eprintln!("\"/run/overlay/merged\" mounted"),
|
Ok(_) => kprintln!("\"/run/overlay/merged\" mounted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match do_mkdir("/run/overlay/merged/oldroot", 0o0700) {
|
match do_mkdir("/run/overlay/merged/oldroot", 0o0700) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("mkdir \"/run/overlay/merged/oldroot\" failed: {}", e);
|
kprintln!("mkdir \"/run/overlay/merged/oldroot\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"/run/overlay/merged/oldroot\" created"),
|
Ok(_) => kprintln!("\"/run/overlay/merged/oldroot\" created"),
|
||||||
}
|
}
|
||||||
|
|
||||||
match pivot_root("/run/overlay/merged", "/run/overlay/merged/oldroot") {
|
match do_pivot_root("/run/overlay/merged", "/run/overlay/merged/oldroot") {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("pivot_root failed: {}", e);
|
kprintln!("pivot_root failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("pivot_root done"),
|
Ok(_) => kprintln!("pivot_root done"),
|
||||||
}
|
}
|
||||||
|
|
||||||
match do_chdir("/") {
|
match do_chdir("/") {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("chdir \"/\" failed: {}", e);
|
kprintln!("chdir \"/\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("chdir \"/\" done"),
|
Ok(_) => kprintln!("chdir \"/\" done"),
|
||||||
}
|
}
|
||||||
|
|
||||||
match do_mount(
|
let move_mnt_pairs = [
|
||||||
Some("/oldroot/run"),
|
("/oldroot/run", "/run", true),
|
||||||
Some("/run"),
|
("/oldroot/dev", "/dev", false),
|
||||||
None,
|
];
|
||||||
MS_REC | MS_MOVE,
|
for (src, dst, required) in move_mnt_pairs {
|
||||||
None,
|
match (
|
||||||
) {
|
do_mount(Some(src), Some(dst), None, MS_REC | MS_MOVE, None),
|
||||||
Err(e) => {
|
required,
|
||||||
eprintln!("moving mountpoint \"/oldroot/run\" failed: {}", e);
|
) {
|
||||||
return Err(());
|
(Err(Errno(EINVAL)), true) => (),
|
||||||
}
|
(Err(e), _) => {
|
||||||
Ok(_) => eprintln!("mountpoint \"/oldroot/run\" moved"),
|
kprintln!("moving mountpoint \"{}\" failed: {}", src, e);
|
||||||
};
|
return Err(());
|
||||||
|
}
|
||||||
|
(Ok(_), _) => kprintln!("mountpoint \"{}\" moved", src),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match do_umount("/oldroot", MNT_DETACH) {
|
match do_umount("/oldroot", MNT_DETACH) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("umount mountpoint \"/oldroot\" failed: {}", e);
|
kprintln!("umount mountpoint \"/oldroot\" failed: {}", e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("mountpoint \"/oldroot/run\" unmounted"),
|
Ok(_) => kprintln!("mountpoint \"/oldroot/run\" unmounted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let unused_dirs = ["/oldroot", "/run/overlay/merged"];
|
let unused_dirs = ["/oldroot", "/run/overlay/merged"];
|
||||||
for path in unused_dirs {
|
for path in unused_dirs {
|
||||||
match do_rmdir(path) {
|
match do_rmdir(path) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("remove \"{}\" failed: {}", path, e);
|
kprintln!("remove \"{}\" failed: {}", path, e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(_) => eprintln!("\"{}\" removed", path),
|
Ok(_) => kprintln!("\"{}\" removed", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,9 +152,10 @@ fn replace_root() -> Result<(), ()> {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn main(_argc: c_int, argv: *mut *const u8) -> c_int {
|
unsafe extern "C" fn main(_argc: c_int, argv: *mut *const u8) -> c_int {
|
||||||
|
init_kmsg();
|
||||||
let start = match do_gettime() {
|
let start = match do_gettime() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{}", err);
|
kprintln!("{}", err);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
@ -294,23 +163,34 @@ unsafe extern "C" fn main(_argc: c_int, argv: *mut *const u8) -> c_int {
|
||||||
|
|
||||||
match replace_root() {
|
match replace_root() {
|
||||||
Err(_) => return EXIT_FAILURE,
|
Err(_) => return EXIT_FAILURE,
|
||||||
Ok(_) => eprintln!("rootfs replaced with overlayfs!"),
|
Ok(_) => kprintln!("rootfs replaced with overlayfs!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let end = match do_gettime() {
|
let end = match do_gettime() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{}", err);
|
kprintln!("{}", err);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
};
|
||||||
let elapsed = end - start;
|
let elapsed = end - start;
|
||||||
eprintln!("processed in {:?}", elapsed);
|
kprintln!("processed in {:?}", elapsed);
|
||||||
|
|
||||||
// overwrite arg0
|
// overwrite arg0
|
||||||
let init_path = CStr::from_bytes_with_nul_unchecked(b"/sbin/init\0");
|
|
||||||
*argv = init_path.as_ptr();
|
|
||||||
execv(init_path.as_ptr(), argv);
|
|
||||||
|
|
||||||
EXIT_SUCCESS
|
let init_candidates = ["/sbin/init", "/usr/sbin/init", "/usr/lib/systemd/systemd"];
|
||||||
|
for init_path in init_candidates {
|
||||||
|
kprintln!("trying to execute \"{}\"", init_path);
|
||||||
|
match do_execv(init_path, argv) {
|
||||||
|
Err(Errno(ENOENT)) | Err(Errno(EACCES)) | Err(Errno(ENOEXEC)) | Err(Errno(ENOTDIR)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
kprintln!("execution \"{}\" failed: {}", init_path, e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(()) => return EXIT_SUCCESS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EXIT_FAILURE
|
||||||
}
|
}
|
||||||
|
|
84
src/mem.rs
84
src/mem.rs
|
@ -1,18 +1,96 @@
|
||||||
use core::alloc::{GlobalAlloc, Layout};
|
use core::alloc::{GlobalAlloc, Layout};
|
||||||
use libc::{c_void, free, malloc};
|
use core::{cmp, mem, ptr};
|
||||||
|
use libc::{c_void, calloc, free, malloc, posix_memalign, realloc};
|
||||||
|
|
||||||
|
const MIN_ALIGN: usize = 16;
|
||||||
|
|
||||||
/// The global allocator type.
|
/// The global allocator type.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Allocator;
|
pub struct Allocator;
|
||||||
|
|
||||||
|
impl Allocator {}
|
||||||
unsafe impl GlobalAlloc for Allocator {
|
unsafe impl GlobalAlloc for Allocator {
|
||||||
|
#[inline]
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
malloc(layout.size()) as *mut u8
|
// jemalloc provides alignment less than MIN_ALIGN for small allocations.
|
||||||
|
// So only rely on MIN_ALIGN if size >= align.
|
||||||
|
// Also see <https://github.com/rust-lang/rust/issues/45955> and
|
||||||
|
// <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>.
|
||||||
|
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
||||||
|
malloc(layout.size()) as *mut u8
|
||||||
|
} else {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
if layout.align() > (1 << 31) {
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aligned_malloc(&layout)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||||
|
// See the comment above in `alloc` for why this check looks the way it does.
|
||||||
|
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
||||||
|
calloc(layout.size(), 1) as *mut u8
|
||||||
|
} else {
|
||||||
|
let ptr = self.alloc(layout);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
ptr::write_bytes(ptr, 0, layout.size());
|
||||||
|
}
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||||
free(ptr as *mut c_void);
|
free(ptr as *mut c_void)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||||
|
if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
|
||||||
|
realloc(ptr as *mut c_void, new_size) as *mut u8
|
||||||
|
} else {
|
||||||
|
realloc_fallback(self, ptr, layout, new_size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The static global allocator.
|
/// The static global allocator.
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL_ALLOCATOR: Allocator = Allocator;
|
static GLOBAL_ALLOCATOR: Allocator = Allocator;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
|
||||||
|
let mut out = ptr::null_mut();
|
||||||
|
// posix_memalign requires that the alignment be a multiple of `sizeof(void*)`.
|
||||||
|
// Since these are all powers of 2, we can just use max.
|
||||||
|
let align = layout.align().max(mem::size_of::<usize>());
|
||||||
|
let ret = posix_memalign(&mut out, align, layout.size());
|
||||||
|
if ret != 0 {
|
||||||
|
ptr::null_mut()
|
||||||
|
} else {
|
||||||
|
out as *mut u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn realloc_fallback(
|
||||||
|
allocator: &Allocator,
|
||||||
|
ptr: *mut u8,
|
||||||
|
old_layout: Layout,
|
||||||
|
new_size: usize,
|
||||||
|
) -> *mut u8 {
|
||||||
|
// Docs for GlobalAlloc::realloc require this to be valid:
|
||||||
|
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||||
|
|
||||||
|
let new_ptr = allocator.alloc(new_layout);
|
||||||
|
if !new_ptr.is_null() {
|
||||||
|
let size = cmp::min(old_layout.size(), new_size);
|
||||||
|
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||||
|
allocator.dealloc(ptr, old_layout);
|
||||||
|
}
|
||||||
|
new_ptr
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
use core::ops::Sub;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use libc::timespec;
|
||||||
|
|
||||||
|
pub(crate) struct Timespec {
|
||||||
|
pub(crate) ts: timespec,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_timespec() -> Timespec {
|
||||||
|
Timespec {
|
||||||
|
ts: timespec {
|
||||||
|
tv_sec: 0,
|
||||||
|
tv_nsec: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<Timespec> for Timespec {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn sub(self, other: Timespec) -> Duration {
|
||||||
|
let sec = self.ts.tv_sec - other.ts.tv_sec;
|
||||||
|
let nsec = self.ts.tv_nsec - other.ts.tv_nsec;
|
||||||
|
if nsec < 0 {
|
||||||
|
Duration::from_secs(sec as u64) - Duration::from_nanos(nsec.unsigned_abs())
|
||||||
|
} else {
|
||||||
|
Duration::from_secs(sec as u64) + Duration::from_nanos(nsec.unsigned_abs())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<timespec> for Timespec {
|
||||||
|
fn from(item: timespec) -> Self {
|
||||||
|
Timespec { ts: item }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<timespec> for Timespec {
|
||||||
|
fn into(self) -> timespec {
|
||||||
|
self.ts
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
use crate::tm::{new_timespec, Timespec};
|
||||||
|
use alloc::ffi::CString;
|
||||||
|
use core::borrow::BorrowMut;
|
||||||
|
use core::ptr::null;
|
||||||
|
use errno::{errno, Errno};
|
||||||
|
use libc::{
|
||||||
|
c_void, chdir, clock_gettime, execv, mkdir, mode_t, mount, rmdir, syscall, umount2,
|
||||||
|
SYS_pivot_root, CLOCK_BOOTTIME,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use libc::{
|
||||||
|
c_uint, makedev, mkdir, mknod, mode_t, mount, readlinkat, rmdir, strlen, syscall, umount2,
|
||||||
|
unlink, SYS_pivot_root, AT_FDCWD, CLOCK_BOOTTIME, S_IFCHR,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) type SystemResult = Result<(), Errno>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn do_mknod(path: &str, major: c_uint, minor: c_uint) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let dev = makedev(major, minor);
|
||||||
|
|
||||||
|
if mknod(raw_path.as_ref().as_ptr(), S_IFCHR, dev) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_mount(
|
||||||
|
source: Option<&str>,
|
||||||
|
target: Option<&str>,
|
||||||
|
fs: Option<&str>,
|
||||||
|
flags: u64,
|
||||||
|
opt: Option<&str>,
|
||||||
|
) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_src = source.map(|v| CString::new(v).ok()).flatten();
|
||||||
|
let raw_tgt = target.map(|v| CString::new(v).ok()).flatten();
|
||||||
|
let raw_fs = fs.map(|v| CString::new(v).ok()).flatten();
|
||||||
|
let raw_fs_opt = opt.map(|v| CString::new(v).ok()).flatten();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if mount(
|
||||||
|
raw_src.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
||||||
|
raw_tgt.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
||||||
|
raw_fs.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null),
|
||||||
|
flags,
|
||||||
|
raw_fs_opt.as_ref().map(|v| v.as_ptr()).unwrap_or_else(null) as *const c_void,
|
||||||
|
) == -1
|
||||||
|
{
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_umount(path: &str, flags: i32) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if umount2(raw_path.as_ref().as_ptr(), flags) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn do_readlink(path: &str) -> Result<CString, Errno> {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
const BUF_SZ: usize = 1024;
|
||||||
|
let mut buf = [0u8; BUF_SZ];
|
||||||
|
if readlinkat(
|
||||||
|
AT_FDCWD,
|
||||||
|
raw_path.as_ref().as_ptr(),
|
||||||
|
buf.as_mut_ptr() as *mut _,
|
||||||
|
buf.len() as size_t,
|
||||||
|
) == -1
|
||||||
|
{
|
||||||
|
return Err(errno());
|
||||||
|
}
|
||||||
|
let c_str_len = strlen(buf.as_ptr() as *const _);
|
||||||
|
Ok(from_utf8_lossy(&buf[..c_str_len]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_mkdir(path: &str, mode: mode_t) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if mkdir(raw_path.as_ref().as_ptr(), mode) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn do_unlink(path: &str) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if unlink(raw_path.as_ref().as_ptr()) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_rmdir(path: &str) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if rmdir(raw_path.as_ref().as_ptr()) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_execv(path: &str, argv: *mut *const u8) -> SystemResult {
|
||||||
|
let raw_init_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
*argv = raw_init_path.as_ref().as_ptr();
|
||||||
|
if execv(raw_init_path.as_ref().as_ptr(), argv) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_chdir(path: &str) -> SystemResult {
|
||||||
|
// has ownership
|
||||||
|
let raw_path = CString::new(path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if chdir(raw_path.as_ref().as_ptr()) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_pivot_root(new_root: &str, put_old: &str) -> SystemResult {
|
||||||
|
let raw_new_root = CString::new(new_root).unwrap();
|
||||||
|
let raw_put_old = CString::new(put_old).unwrap();
|
||||||
|
unsafe {
|
||||||
|
if syscall(
|
||||||
|
SYS_pivot_root,
|
||||||
|
raw_new_root.as_ref().as_ptr(),
|
||||||
|
raw_put_old.as_ref().as_ptr(),
|
||||||
|
) == -1
|
||||||
|
{
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_gettime() -> Result<Timespec, Errno> {
|
||||||
|
let mut time = new_timespec();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if clock_gettime(CLOCK_BOOTTIME, time.ts.borrow_mut()) == -1 {
|
||||||
|
Err(errno())
|
||||||
|
} else {
|
||||||
|
Ok(time.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[inline]
|
||||||
|
fn from_utf8_lossy(input: &[u8]) -> &str {
|
||||||
|
match str::from_utf8(input) {
|
||||||
|
Ok(valid) => valid,
|
||||||
|
Err(error) => unsafe { str::from_utf8_unchecked(&input[..error.valid_up_to()]) },
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue