working on forking

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-07-09 14:34:10 +02:00
parent 937921aae6
commit 61bfa35549
5 changed files with 67 additions and 55 deletions

View File

@ -11,8 +11,8 @@ use std::panic::UnwindSafe;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::future::poll_fn;
use futures::io::AsyncRead;
use nix::errno::Errno;
use crate::syscall::SyscallStatus;
use crate::tools::Fd;
@ -20,16 +20,12 @@ use crate::{libc_try, libc_wrap};
pub async fn forking_syscall<F>(func: F) -> io::Result<SyscallStatus>
where
F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe,
F: FnOnce() -> Result<i64, Errno> + UnwindSafe,
{
let mut fork = Fork::new(func)?;
let mut buf = [0u8; 10];
use futures::io::AsyncReadExt;
fork.read_exact(&mut buf).await?;
let result = fork.get_result().await?;
fork.wait()?;
Ok(SyscallStatus::Err(libc::ENOENT))
Ok(result)
}
pub struct Fork {
@ -47,53 +43,47 @@ impl Drop for Fork {
}
}
#[repr(C, packed)]
struct Data {
val: i64,
error: i32,
}
impl Fork {
pub fn new<F>(func: F) -> io::Result<Self>
where
F: FnOnce() -> io::Result<SyscallStatus> + UnwindSafe,
F: FnOnce() -> Result<i64, Errno> + UnwindSafe,
{
let mut pipe: [c_int; 2] = [0, 0];
libc_try!(unsafe { libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) });
let (pipe_r, pipe_w) = (Fd(pipe[0]), Fd(pipe[1]));
let pipe_r = match crate::tools::GenericStream::from_fd(pipe_r) {
Ok(o) => o,
Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.to_string())),
};
let pid = libc_try!(unsafe { libc::fork() });
if pid == 0 {
std::mem::drop(pipe_r);
let mut pipe_w = unsafe { std::fs::File::from_raw_fd(pipe_w.into_raw_fd()) };
let _ = std::panic::catch_unwind(move || {
let mut buf = [0u8; 10];
let out = match func() {
Ok(val) => Data {
val,
error: 0,
},
Err(error) => Data {
val: -1,
error: error as _,
},
};
match func() {
Ok(SyscallStatus::Ok(value)) => unsafe {
std::ptr::write(buf.as_mut_ptr().add(1) as *mut i64, value);
},
Ok(SyscallStatus::Err(value)) => {
buf[0] = 1;
unsafe {
std::ptr::write(buf.as_mut_ptr().add(1) as *mut i32, value);
}
}
Err(err) => match err.raw_os_error() {
Some(err) => {
buf[0] = 2;
unsafe {
std::ptr::write(buf.as_mut_ptr().add(1) as *mut i32, err);
}
}
None => {
buf[0] = 3;
}
},
}
let slice = unsafe {
std::slice::from_raw_parts(
&out as *const Data as *const u8,
std::mem::size_of::<Data>(),
)
};
use std::io::Write;
match pipe_w.write_all(&buf) {
match pipe_w.write_all(slice) {
Ok(()) => unsafe { libc::_exit(0) },
Err(_) => unsafe { libc::_exit(1) },
}
@ -103,6 +93,11 @@ impl Fork {
}
}
let pipe_r = match crate::tools::GenericStream::from_fd(pipe_r) {
Ok(o) => o,
Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.to_string())),
};
Ok(Self {
pid: Some(pid),
out: pipe_r,
@ -111,9 +106,9 @@ impl Fork {
pub fn wait(&mut self) -> io::Result<()> {
let my_pid = self.pid.take().unwrap();
let mut status: c_int = -1;
loop {
let mut status: c_int = -1;
match libc_wrap!(unsafe { libc::waitpid(my_pid, &mut status, 0) }) {
Ok(pid) if pid == my_pid => break,
Ok(_other) => continue,
@ -122,11 +117,28 @@ impl Fork {
}
}
Ok(())
if status != 0 {
Err(io::Error::new(io::ErrorKind::Other, "error in child process"))
} else {
Ok(())
}
}
pub async fn async_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
poll_fn(|cx| Pin::new(&mut *self).poll_read(cx, buf)).await
pub async fn get_result(&mut self) -> io::Result<SyscallStatus> {
use futures::io::AsyncReadExt;
let mut data: Data = unsafe { std::mem::zeroed() };
self.read_exact(unsafe {
std::slice::from_raw_parts_mut(
&mut data as *mut Data as *mut u8,
std::mem::size_of::<Data>(),
)
}).await?;
if data.error == 0 {
Ok(SyscallStatus::Ok(data.val))
} else {
Ok(SyscallStatus::Err(data.error))
}
}
}

View File

@ -295,13 +295,8 @@ impl ProxyMessageBuffer {
}
/// Checked way to get a `mode_t` argument.
pub fn arg_mode_t(&self, arg: u32) -> Result<nix::sys::stat::Mode, Error> {
use nix::sys::stat;
stat::Mode::from_bits(
stat::mode_t::try_from(self.arg(arg)?).map_err(|_| Error::from(Errno::EINVAL))?,
)
.ok_or_else(|| Errno::EINVAL.into())
pub fn arg_mode_t(&self, arg: u32) -> Result<nix::sys::stat::mode_t, Error> {
nix::sys::stat::mode_t::try_from(self.arg(arg)?).map_err(|_| Error::from(Errno::EINVAL))
}
/// Checked way to get a `dev_t` argument.

View File

@ -44,10 +44,10 @@ impl PidFd {
}
pub fn fd_cwd(&self) -> io::Result<Fd> {
self.fd(b"cwd", libc::O_DIRECTORY)
self.fd(b"cwd\0", libc::O_DIRECTORY)
}
pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<Fd> {
self.fd(format!("fd/{}", num).as_bytes(), flags)
self.fd(format!("fd/{}\0", num).as_bytes(), flags)
}
}

View File

@ -4,6 +4,7 @@ use failure::Error;
use nix::errno::Errno;
use nix::sys::stat;
use crate::fork::forking_syscall;
use crate::lxcseccomp::ProxyMessageBuffer;
use crate::syscall::SyscallStatus;
use crate::tools::Fd;
@ -27,9 +28,11 @@ pub async fn mknodat(msg: &ProxyMessageBuffer) -> Result<SyscallStatus, Error> {
async fn do_mknodat(
_dirfd: Fd,
_pathname: CString,
_mode: stat::Mode,
_mode: stat::mode_t,
_dev: stat::dev_t,
) -> Result<SyscallStatus, Error> {
println!("=> Responding with ENOENT");
Err(Errno::ENOENT.into())
Ok(forking_syscall(move || {
Err(Errno::ENOENT)
}).await?)
}

View File

@ -30,8 +30,10 @@ macro_rules! file_descriptor_impl {
($type:ty) => {
impl Drop for $type {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
if self.0 >= 0 {
unsafe {
libc::close(self.0);
}
}
}
}