From 3aa61be7cd0eeb52d6ef8787828b738a83a199af Mon Sep 17 00:00:00 2001 From: Avril Date: Fri, 27 Aug 2021 19:48:37 +0100 Subject: [PATCH] Added `copy_` versions of `move_write/read_value()`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for comfork's current commit: Middle blessing − 中吉 --- src/sys/fork.rs | 58 +++++------------------------ src/sys/pipe.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 50 deletions(-) diff --git a/src/sys/fork.rs b/src/sys/fork.rs index 5b88904..fce3eab 100644 --- a/src/sys/fork.rs +++ b/src/sys/fork.rs @@ -13,15 +13,18 @@ use std::io::{ Write, }; use std::marker::Unpin; -use std::panic::{ - AssertUnwindSafe, - catch_unwind, - resume_unwind, -}; use errno::Errno; -/// THe kind of error from forking +/// Information on how to set-up child process. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ForkInfo +{ + uid: Option, + gid: Option, +} + +/// The kind of error from forking #[derive(Debug)] #[non_exhaustive] pub enum ErrorKind @@ -38,49 +41,6 @@ pub enum ErrorKind Pipe(pipe::ErrorKind), } -// TODO: Add non-`sys` type: `Channel`, which can be used to send/recv typed data between parent and child. -// It should leverage the two below functions: `move_*_stream()`. - -/// Move a value into a stream. -/// -/// This function will return `Ok` if and only if all bytes from `val` were successfully written to the stream `to`. -/// If only a subset of them were written, then the data is corrupt and that is counted as an error. -/// -/// # Safety -/// The object `val` is written into the stream `to` verbatim. Its destructor is not run. -/// The object should be read verbatim from the stream with no errors or corruption before its dropped, or it will leak resources. -/// -/// This function is safe, but reading from the stream into a new object is not. -/// -/// ## Unpin -/// `T` should be `Unpin` in case it contains self-referrential data, as those references will no longer be valid after being moved across a stream. -// TODO: Should we expose these to the user somehow, with an unsafe helper module maybe? They could be useful. -// Or perhaps: Add the functionality to move types to/from the child/parent into the library, similar to `mpsc`. -pub(crate) fn move_write_value(mut to: S, val: T) -> io::Result<()> -{ - let bytes = unsafe { - std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::()) - }; - // Catch any panics that may happen in `write`. - let res = catch_unwind(AssertUnwindSafe(move || { - to.write_all(bytes) - })); - // Ensure this happens even if the above panics. - drop(bytes); // Make sure `bytes` isn't dangling after we move `val` into `forget()`. - std::mem::forget(val); - // Resume unwind if one occoured, if not return the value - match res { - Ok(v) => v, - Err(panic) => resume_unwind(panic), - } -} - -//TODO: Counterpart to `move_write_value()`. This function should be `unsafe` as it is extermely unsafe. -pub(crate) unsafe fn move_read_value(mut from: S) -> io::Result -{ - todo!() -} - /// An error in either forking or communicating with the child process during its setup. #[derive(Debug)] #[repr(transparent)] diff --git a/src/sys/pipe.rs b/src/sys/pipe.rs index c9d3571..a8e4224 100644 --- a/src/sys/pipe.rs +++ b/src/sys/pipe.rs @@ -5,7 +5,103 @@ use super::*; use errno::Errno; use std::{error, fmt}; use std::ops::Drop; -use std::io; +use std::io::{ + self, + Read, Write, +}; +use std::panic::{ + AssertUnwindSafe, + catch_unwind, + resume_unwind, +}; + +// TODO: Add non-`sys` type: `Channel`, which can be used to send/recv typed data between parent and child. +// It should leverage the two below functions: `move_*_stream()`. + +/// Move a value into a stream. +/// +/// This function will return `Ok` if and only if all bytes from `val` were successfully written to the stream `to`. +/// If only a subset of them were written, then the data is corrupt and that is counted as an error. +/// +/// # Safety +/// The object `val` is written into the stream `to` verbatim. Its destructor is not run. +/// The object should be read verbatim from the stream with no errors or corruption before its dropped, or it will leak resources. +/// +/// This function is safe, but reading from the stream into a new object is not. +/// +/// ## Unpin +/// `T` should be `Unpin` in case it contains self-referrential data, as those references will no longer be valid after being moved across a stream. +// TODO: Should we expose these to the user somehow, with an unsafe helper module maybe? They could be useful. +// Or perhaps: Add the functionality to move types to/from the child/parent into the library, similar to `mpsc`. + +// XXX: This is not safe, and will cause a memory leak when moving from parent to child. If a box is passed to the child, then it will not be deep copied, nor will it be correctly freed on the parent. Idk how to handle this for complex types... +pub(crate) fn move_write_value(mut to: S, val: T) -> io::Result<()> +{ + let bytes = unsafe { + std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::()) + }; + // Catch any panics that may happen in `write`. + let res = catch_unwind(AssertUnwindSafe(move || { + to.write_all(bytes) + })); + // Ensure this happens even if the above panics. + drop(bytes); // Make sure `bytes` isn't dangling after we move `val` into `forget()`. + std::mem::forget(val); + // Resume unwind if one occoured, if not return the value + match res { + Ok(v) => v, + Err(panic) => resume_unwind(panic), + } +} + +/// Write a value into a stream. The value can be unsized but must be `Send` as we cannot guarantee the stream will not be read on a seperate thread. +/// +/// This is a safe function, but reading from the other stream is not. +/// Ownership is not transfered to the other side of the stream, so make sure that if the value is not `Copy` any resources owned by `val` are properly transfered to the read object. +// Should this require `Send` as a bound? +pub(crate) fn copy_write_value(mut to: S, val: &T) -> io::Result +{ + let sz = std::mem::size_of_val(val); + let bytes = unsafe { + std::slice::from_raw_parts(val as *const T as *const _, sz) + }; + to.write_all(bytes)?; + Ok(sz) +} + +/// Read a value from a stream. +/// +/// # Safety +/// This is extremely unsafe, it reads the type `T` bitwise from the stream. +/// The caller must ensure that any pointers contained within `T` remain valid after being passed through a stream, and that the type passed through has not been corrupted within the stream. +pub(crate) unsafe fn copy_read_value(mut from: S) -> io::Result +{ + let mut value = std::mem::MaybeUninit::::uninit(); + Ok({ + let bytes = { + std::slice::from_raw_parts_mut(value.as_mut_ptr() as *mut _ as *mut _, std::mem::size_of::()) + }; + from.read_exact(&mut bytes[..])?; + { + value.assume_init() + } + }) +} + + +/// Read a value from a stream. +/// +/// This is currently identical to `copy_read_value()`. +/// +/// # Safety +/// This is extremely unsafe, it reads the type `T` bitwise from the stream. +/// The caller must ensure that any pointers contained within `T` remain valid after being passed through a stream, and that the type passed through has not been corrupted within the stream. +#[inline] pub(crate) unsafe fn move_read_value(from: S) -> io::Result +{ + // These are effectively the same. + copy_read_value(from) +} + /// Kind of pipe error #[derive(Debug)]