diff --git a/Dockerfile b/Dockerfile index e8a2c06..2d74ae2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ LABEL org.opencontainers.image.authors="Sangbum Kim " WORKDIR /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 && \ apk add --no-cache \ @@ -22,6 +22,7 @@ RUN set -x && \ RUN set -x && \ cargo build --release && \ ls -alh target/release/init-wrapper + # ldd target/release/init-wrapper && \ # && \ # ldd target/release/init-wrapper diff --git a/README.md b/README.md index 63a2de0..86d957b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ mount -t overlay -o rw,relatime,lowerdir=/run/overlay/lower,upperdir=/run/overla mkdir -p /run/overlay/merged/oldroot pivot_root /run/overlay/merged /run/overlay/merged/oldroot cd / +mount --move /oldroot/dev /dev || true mount --move /oldroot/run /run umount -l /oldroot rmdir /oldroot diff --git a/src/kmsg.rs b/src/kmsg.rs new file mode 100644 index 0000000..974ad7a --- /dev/null +++ b/src/kmsg.rs @@ -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(); + } + } + } + }; +} diff --git a/src/link.rs b/src/link.rs index 864bcd5..fd7fd2c 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types, dead_code)] -use libc::FILE; +use libc::{exit, FILE}; #[link(name = "c")] extern "C" { @@ -9,9 +9,16 @@ extern "C" { static stderr: *mut FILE; } +#[no_mangle] +extern "C" fn rust_eh_personality() {} +#[no_mangle] +unsafe extern "C" fn _Unwind_Resume() -> ! { + exit(2) +} + #[panic_handler] #[inline(never)] #[cfg(not(test))] unsafe fn panic_handler(_: &core::panic::PanicInfo) -> ! { - libc::exit(2) + exit(2) } diff --git a/src/main.rs b/src/main.rs index 1e99bd5..ff83ba1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,167 +2,30 @@ #![no_main] extern crate alloc; +mod kmsg; mod link; mod mem; +mod tm; +mod unix; +use errno::Errno; -use alloc::ffi::CString; -use core::ffi::CStr; -use core::ops::Sub; -use core::ptr::null; -use core::time::Duration; -use errno::{errno, Errno}; +use kmsg::init_kmsg; use libc::{ - c_int, c_void, chdir, clock_gettime, execv, mkdir, mode_t, mount, rmdir, syscall, timespec, - umount2, SYS_pivot_root, CLOCK_BOOTTIME, EXIT_FAILURE, EXIT_SUCCESS, MNT_DETACH, MS_BIND, - MS_LAZYTIME, MS_MOVE, MS_NODEV, MS_NOSUID, MS_PRIVATE, MS_RDONLY, MS_REC, MS_RELATIME, + c_int, EACCES, EINVAL, ENOENT, ENOEXEC, ENOTDIR, EXIT_FAILURE, EXIT_SUCCESS, MNT_DETACH, + MS_BIND, MS_LAZYTIME, MS_MOVE, MS_NODEV, MS_NOSUID, MS_PRIVATE, MS_RDONLY, MS_REC, MS_RELATIME, }; -use libc_print::std_name::eprintln; -struct Timespec { - ts: timespec, -} - -type SystemResult = Result<(), Errno>; - -impl Sub 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 for Timespec { - fn from(item: timespec) -> Self { - Timespec { ts: item } - } -} - -impl Into 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 { - 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()) - } - } -} +use unix::{ + do_chdir, do_execv, do_gettime, do_mkdir, do_mount, do_pivot_root, do_rmdir, do_umount, +}; fn replace_root() -> Result<(), ()> { match do_mount(None, Some("/"), None, MS_REC | MS_PRIVATE, None) { Err(e) => { - eprintln!("remount \"/\" with private option failed: {}", e); + kprintln!("remount \"/\" with private option failed: {}", e); return Err(()); } - Ok(_) => eprintln!("\"/\" remounted"), + Ok(_) => kprintln!("\"/\" remounted"), }; match do_mount( @@ -173,10 +36,10 @@ fn replace_root() -> Result<(), ()> { None, ) { Err(e) => { - eprintln!("mount \"/run\" failed: {}", e); + kprintln!("mount \"/run\" failed: {}", e); return Err(()); } - Ok(_) => eprintln!("\"/run\" mounted"), + Ok(_) => kprintln!("\"/run\" mounted"), }; let ovr_dir_structures = [ @@ -186,13 +49,14 @@ fn replace_root() -> Result<(), ()> { ("/run/overlay/work", 0o0755), ("/run/overlay/merged", 0o0755), ]; + for (path, mode) in ovr_dir_structures { match do_mkdir(path, mode) { Err(e) => { - eprintln!("mkdir \"{}\" failed: {}", path, e); + kprintln!("mkdir \"{}\" failed: {}", path, e); return Err(()); } - Ok(_) => eprintln!("\"{}\" created", path), + Ok(_) => kprintln!("\"{}\" created", path), } } @@ -204,10 +68,10 @@ fn replace_root() -> Result<(), ()> { None, ) { Err(e) => { - eprintln!("mount \"/run/overlay/lower\" failed: {}", e); + kprintln!("mount \"/run/overlay/lower\" failed: {}", e); return Err(()); } - Ok(_) => eprintln!("\"/run/overlay/lower\" mounted"), + Ok(_) => kprintln!("\"/run/overlay/lower\" mounted"), }; match do_mount( @@ -216,66 +80,70 @@ fn replace_root() -> Result<(), ()> { 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"), ) { - Err(e) => {eprintln!("mount \"/run/overlay/merged\" failed: {}", e); + Err(e) => {kprintln!("mount \"/run/overlay/merged\" failed: {}", e); return Err(()); }, - Ok(_) => eprintln!("\"/run/overlay/merged\" mounted"), + Ok(_) => kprintln!("\"/run/overlay/merged\" mounted"), }; match do_mkdir("/run/overlay/merged/oldroot", 0o0700) { Err(e) => { - eprintln!("mkdir \"/run/overlay/merged/oldroot\" failed: {}", e); + kprintln!("mkdir \"/run/overlay/merged/oldroot\" failed: {}", e); 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) => { - eprintln!("pivot_root failed: {}", e); + kprintln!("pivot_root failed: {}", e); return Err(()); } - Ok(_) => eprintln!("pivot_root done"), + Ok(_) => kprintln!("pivot_root done"), } match do_chdir("/") { Err(e) => { - eprintln!("chdir \"/\" failed: {}", e); + kprintln!("chdir \"/\" failed: {}", e); return Err(()); } - Ok(_) => eprintln!("chdir \"/\" done"), + Ok(_) => kprintln!("chdir \"/\" done"), } - match do_mount( - Some("/oldroot/run"), - Some("/run"), - None, - MS_REC | MS_MOVE, - None, - ) { - Err(e) => { - eprintln!("moving mountpoint \"/oldroot/run\" failed: {}", e); - return Err(()); - } - Ok(_) => eprintln!("mountpoint \"/oldroot/run\" moved"), - }; + let move_mnt_pairs = [ + ("/oldroot/run", "/run", true), + ("/oldroot/dev", "/dev", false), + ]; + for (src, dst, required) in move_mnt_pairs { + match ( + do_mount(Some(src), Some(dst), None, MS_REC | MS_MOVE, None), + required, + ) { + (Err(Errno(EINVAL)), true) => (), + (Err(e), _) => { + kprintln!("moving mountpoint \"{}\" failed: {}", src, e); + return Err(()); + } + (Ok(_), _) => kprintln!("mountpoint \"{}\" moved", src), + }; + } match do_umount("/oldroot", MNT_DETACH) { Err(e) => { - eprintln!("umount mountpoint \"/oldroot\" failed: {}", e); + kprintln!("umount mountpoint \"/oldroot\" failed: {}", e); return Err(()); } - Ok(_) => eprintln!("mountpoint \"/oldroot/run\" unmounted"), + Ok(_) => kprintln!("mountpoint \"/oldroot/run\" unmounted"), }; let unused_dirs = ["/oldroot", "/run/overlay/merged"]; for path in unused_dirs { match do_rmdir(path) { Err(e) => { - eprintln!("remove \"{}\" failed: {}", path, e); + kprintln!("remove \"{}\" failed: {}", path, e); return Err(()); } - Ok(_) => eprintln!("\"{}\" removed", path), + Ok(_) => kprintln!("\"{}\" removed", path), } } @@ -284,9 +152,10 @@ fn replace_root() -> Result<(), ()> { #[no_mangle] unsafe extern "C" fn main(_argc: c_int, argv: *mut *const u8) -> c_int { + init_kmsg(); let start = match do_gettime() { Err(err) => { - eprintln!("{}", err); + kprintln!("{}", err); return EXIT_FAILURE; } Ok(v) => v, @@ -294,23 +163,34 @@ unsafe extern "C" fn main(_argc: c_int, argv: *mut *const u8) -> c_int { match replace_root() { Err(_) => return EXIT_FAILURE, - Ok(_) => eprintln!("rootfs replaced with overlayfs!"), + Ok(_) => kprintln!("rootfs replaced with overlayfs!"), }; let end = match do_gettime() { Err(err) => { - eprintln!("{}", err); + kprintln!("{}", err); return EXIT_FAILURE; } Ok(v) => v, }; let elapsed = end - start; - eprintln!("processed in {:?}", elapsed); + kprintln!("processed in {:?}", elapsed); // 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 } diff --git a/src/mem.rs b/src/mem.rs index 661c343..757c167 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,18 +1,96 @@ 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. #[derive(Default)] pub struct Allocator; +impl Allocator {} unsafe impl GlobalAlloc for Allocator { + #[inline] 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 and + // . + 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) { - 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. #[global_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::()); + 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 +} diff --git a/src/tm.rs b/src/tm.rs new file mode 100644 index 0000000..4641dab --- /dev/null +++ b/src/tm.rs @@ -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 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 for Timespec { + fn from(item: timespec) -> Self { + Timespec { ts: item } + } +} + +impl Into for Timespec { + fn into(self) -> timespec { + self.ts + } +} diff --git a/src/unix.rs b/src/unix.rs new file mode 100644 index 0000000..7674e04 --- /dev/null +++ b/src/unix.rs @@ -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 { + // 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 { + 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()]) }, + } +}