restructure StringArrayWriter trait into StringArray struct

This commit is contained in:
Pat Hickey
2020-09-02 14:11:32 -07:00
parent b63e974014
commit 39f1c9716c
5 changed files with 111 additions and 91 deletions

View File

@@ -1,7 +1,7 @@
use crate::entry::{Entry, EntryHandle}; use crate::entry::{Entry, EntryHandle};
use crate::fdpool::FdPool; use crate::fdpool::FdPool;
use crate::handle::Handle; use crate::handle::Handle;
use crate::string_array_writer::StringArrayWriter; use crate::string_array::{StringArray, StringArrayError};
use crate::sys::osdir::OsDir; use crate::sys::osdir::OsDir;
use crate::sys::stdio::NullDevice; use crate::sys::stdio::NullDevice;
use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt}; use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt};
@@ -12,7 +12,7 @@ use std::borrow::Borrow;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ffi::{self, CString, OsString}; use std::ffi::OsString;
use std::fs::File; use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
@@ -33,9 +33,12 @@ pub enum WasiCtxBuilderError {
/// This error is expected to only occur on Windows hosts. /// This error is expected to only occur on Windows hosts.
#[error("provided sequence is not valid UTF-16: {0}")] #[error("provided sequence is not valid UTF-16: {0}")]
InvalidUtf16(#[from] string::FromUtf16Error), InvalidUtf16(#[from] string::FromUtf16Error),
/// Provided sequence of bytes contained an unexpected NUL byte. /// Error constructing arguments
#[error("provided sequence contained an unexpected NUL byte")] #[error("while constructing arguments: {0}")]
UnexpectedNul(#[from] ffi::NulError), Args(#[source] StringArrayError),
/// Error constructing environment
#[error("while constructing environment: {0}")]
Env(#[source] StringArrayError),
/// The root of a VirtualDirEntry tree must be a VirtualDirEntry::Directory. /// The root of a VirtualDirEntry tree must be a VirtualDirEntry::Directory.
#[error("the root of a VirtualDirEntry tree at {} must be a VirtualDirEntry::Directory", .0.display())] #[error("the root of a VirtualDirEntry tree at {} must be a VirtualDirEntry::Directory", .0.display())]
VirtualDirEntryRootNotADirectory(PathBuf), VirtualDirEntryRootNotADirectory(PathBuf),
@@ -65,24 +68,24 @@ impl std::fmt::Debug for PendingEntry {
} }
#[derive(Debug, Eq, Hash, PartialEq)] #[derive(Debug, Eq, Hash, PartialEq)]
enum PendingCString { enum PendingString {
Bytes(Vec<u8>), Bytes(Vec<u8>),
OsString(OsString), OsString(OsString),
} }
impl From<Vec<u8>> for PendingCString { impl From<Vec<u8>> for PendingString {
fn from(bytes: Vec<u8>) -> Self { fn from(bytes: Vec<u8>) -> Self {
Self::Bytes(bytes) Self::Bytes(bytes)
} }
} }
impl From<OsString> for PendingCString { impl From<OsString> for PendingString {
fn from(s: OsString) -> Self { fn from(s: OsString) -> Self {
Self::OsString(s) Self::OsString(s)
} }
} }
impl PendingCString { impl PendingString {
fn into_string(self) -> WasiCtxBuilderResult<String> { fn into_string(self) -> WasiCtxBuilderResult<String> {
let res = match self { let res = match self {
Self::Bytes(v) => String::from_utf8(v)?, Self::Bytes(v) => String::from_utf8(v)?,
@@ -100,13 +103,6 @@ impl PendingCString {
}; };
Ok(res) Ok(res)
} }
/// Create a `CString` containing valid UTF-8.
fn into_utf8_cstring(self) -> WasiCtxBuilderResult<CString> {
let s = self.into_string()?;
let s = CString::new(s)?;
Ok(s)
}
} }
struct PendingPreopen(Box<dyn FnOnce() -> WasiCtxBuilderResult<Box<dyn Handle>>>); struct PendingPreopen(Box<dyn FnOnce() -> WasiCtxBuilderResult<Box<dyn Handle>>>);
@@ -130,8 +126,8 @@ pub struct WasiCtxBuilder {
stdout: Option<PendingEntry>, stdout: Option<PendingEntry>,
stderr: Option<PendingEntry>, stderr: Option<PendingEntry>,
preopens: Option<Vec<(PathBuf, PendingPreopen)>>, preopens: Option<Vec<(PathBuf, PendingPreopen)>>,
args: Option<Vec<PendingCString>>, args: Option<Vec<PendingString>>,
env: Option<HashMap<PendingCString, PendingCString>>, env: Option<HashMap<PendingString, PendingString>>,
} }
impl WasiCtxBuilder { impl WasiCtxBuilder {
@@ -180,7 +176,7 @@ impl WasiCtxBuilder {
pub fn inherit_args(&mut self) -> &mut Self { pub fn inherit_args(&mut self) -> &mut Self {
let args = self.args.as_mut().unwrap(); let args = self.args.as_mut().unwrap();
args.clear(); args.clear();
args.extend(env::args_os().map(PendingCString::OsString)); args.extend(env::args_os().map(PendingString::OsString));
self self
} }
@@ -324,16 +320,16 @@ impl WasiCtxBuilder {
/// If any of the arguments or environment variables in this builder cannot be converted into /// If any of the arguments or environment variables in this builder cannot be converted into
/// `CString`s, either due to NUL bytes or Unicode conversions, this will fail. /// `CString`s, either due to NUL bytes or Unicode conversions, this will fail.
pub fn build(&mut self) -> WasiCtxBuilderResult<WasiCtx> { pub fn build(&mut self) -> WasiCtxBuilderResult<WasiCtx> {
// Process arguments and environment variables into `CString`s, failing quickly if they // Process arguments and environment variables into `String`s, failing quickly if they
// contain any NUL bytes, or if conversion from `OsString` fails. // contain any NUL bytes, or if conversion from `OsString` fails.
let args = self let args = self
.args .args
.take() .take()
.unwrap() .unwrap()
.into_iter() .into_iter()
.map(|arg| arg.into_utf8_cstring()) .map(|arg| arg.into_string())
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?; .collect::<WasiCtxBuilderResult<Vec<String>>>()?;
let args = StringArray::new(args).map_err(WasiCtxBuilderError::Args)?;
let env = self let env = self
.env .env
.take() .take()
@@ -344,14 +340,12 @@ impl WasiCtxBuilder {
v.into_string().and_then(|v| { v.into_string().and_then(|v| {
pair.push('='); pair.push('=');
pair.push_str(v.as_str()); pair.push_str(v.as_str());
// We have valid UTF-8, but the keys and values have not yet been checked Ok(pair)
// for NULs, so we do a final check here.
let s = CString::new(pair)?;
Ok(s)
}) })
}) })
}) })
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?; .collect::<WasiCtxBuilderResult<Vec<String>>>()?;
let env = StringArray::new(env).map_err(WasiCtxBuilderError::Env)?;
let mut entries = EntryTable::new(); let mut entries = EntryTable::new();
// Populate the non-preopen entries. // Populate the non-preopen entries.
@@ -441,8 +435,8 @@ impl EntryTable {
pub struct WasiCtx { pub struct WasiCtx {
entries: RefCell<EntryTable>, entries: RefCell<EntryTable>,
pub(crate) args: Vec<CString>, pub(crate) args: StringArray,
pub(crate) env: Vec<CString>, pub(crate) env: StringArray,
} }
impl WasiCtx { impl WasiCtx {
@@ -493,6 +487,7 @@ impl WasiCtx {
self.entries.borrow_mut().remove(fd).ok_or(Error::Badf) self.entries.borrow_mut().remove(fd).ok_or(Error::Badf)
} }
/*
pub(crate) fn args(&self) -> &impl StringArrayWriter { pub(crate) fn args(&self) -> &impl StringArrayWriter {
&self.args &self.args
} }
@@ -500,4 +495,5 @@ impl WasiCtx {
pub(crate) fn env(&self) -> &impl StringArrayWriter { pub(crate) fn env(&self) -> &impl StringArrayWriter {
&self.env &self.env
} }
*/
} }

View File

@@ -32,7 +32,7 @@ mod path;
mod poll; mod poll;
mod sandboxed_tty_writer; mod sandboxed_tty_writer;
pub mod snapshots; pub mod snapshots;
mod string_array_writer; mod string_array;
mod sys; mod sys;
pub mod virtfs; pub mod virtfs;
pub mod wasi; pub mod wasi;

View File

@@ -1,6 +1,5 @@
use crate::entry::{Entry, EntryHandle}; use crate::entry::{Entry, EntryHandle};
use crate::handle::HandleRights; use crate::handle::HandleRights;
use crate::string_array_writer::StringArrayWriter;
use crate::sys::clock; use crate::sys::clock;
use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1; use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1;
use crate::wasi::{types, AsBytes}; use crate::wasi::{types, AsBytes};
@@ -17,12 +16,11 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
argv: &GuestPtr<'b, GuestPtr<'b, u8>>, argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
argv_buf: &GuestPtr<'b, u8>, argv_buf: &GuestPtr<'b, u8>,
) -> Result<()> { ) -> Result<()> {
self.args().write_to_guest(argv_buf, argv) self.args.write_to_guest(argv_buf, argv)
} }
fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> { fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> {
let args = self.args(); Ok((self.args.number_elements, self.args.cumulative_size))
Ok((args.number_elements()?, args.cumulative_size()?))
} }
fn environ_get<'b>( fn environ_get<'b>(
@@ -30,12 +28,11 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
environ: &GuestPtr<'b, GuestPtr<'b, u8>>, environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
environ_buf: &GuestPtr<'b, u8>, environ_buf: &GuestPtr<'b, u8>,
) -> Result<()> { ) -> Result<()> {
self.env().write_to_guest(environ_buf, environ) self.env.write_to_guest(environ_buf, environ)
} }
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> { fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> {
let args = self.args(); Ok((self.env.number_elements, self.env.cumulative_size))
Ok((args.number_elements()?, args.cumulative_size()?))
} }
fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp> { fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp> {

View File

@@ -0,0 +1,80 @@
use crate::Error;
use std::convert::TryInto;
use std::ffi::CString;
use wiggle::GuestPtr;
pub struct StringArray {
elems: Vec<CString>,
pub number_elements: u32,
pub cumulative_size: u32,
}
#[derive(Debug, thiserror::Error)]
pub enum StringArrayError {
/// Provided sequence of bytes contained an unexpected NUL byte.
#[error("provided sequence contained an unexpected NUL byte")]
Nul(#[from] std::ffi::NulError),
/// Too many elements: must fit into u32
#[error("too many elements")]
NumElements,
/// Element size: must fit into u32
#[error("element too big")]
ElemSize,
/// Cumulative element size: must fit into u32
#[error("cumulative element size too big")]
CumElemSize,
}
impl StringArray {
pub fn new(elems: Vec<String>) -> Result<Self, StringArrayError> {
let elems = elems
.into_iter()
.map(|s| CString::new(s))
.collect::<Result<Vec<CString>, _>>()?;
let number_elements = elems
.len()
.try_into()
.map_err(|_| StringArrayError::NumElements)?;
let mut cumulative_size: u32 = 0;
for elem in elems.iter() {
let elem_bytes = elem
.as_bytes_with_nul()
.len()
.try_into()
.map_err(|_| StringArrayError::ElemSize)?;
cumulative_size = cumulative_size
.checked_add(elem_bytes)
.ok_or(StringArrayError::CumElemSize)?;
}
Ok(Self {
elems,
number_elements,
cumulative_size,
})
}
pub fn write_to_guest<'a>(
&self,
buffer: &GuestPtr<'a, u8>,
element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>,
) -> Result<(), Error> {
let element_heads = element_heads.as_array(self.number_elements);
let buffer = buffer.as_array(self.cumulative_size);
let mut cursor = 0;
for (elem, head) in self.elems.iter().zip(element_heads.iter()) {
let bytes = elem.as_bytes_with_nul();
let len: u32 = bytes.len().try_into()?;
let elem_buffer = buffer
.get_range(cursor..(cursor + len))
.ok_or(Error::Inval)?; // Elements don't fit in buffer provided
elem_buffer.copy_from_slice(bytes)?;
head?.write(
elem_buffer
.get(0)
.expect("all elem buffers at least length 1"),
)?;
cursor += len;
}
Ok(())
}
}

View File

@@ -1,53 +0,0 @@
use crate::{Error, Result};
use std::convert::TryInto;
use std::ffi::CString;
use wiggle::GuestPtr;
pub trait StringArrayWriter {
fn number_elements(&self) -> Result<u32>;
fn cumulative_size(&self) -> Result<u32>;
fn write_to_guest<'a>(
&self,
buffer: &GuestPtr<'a, u8>,
elements: &GuestPtr<'a, GuestPtr<'a, u8>>,
) -> Result<()>;
}
impl StringArrayWriter for Vec<CString> {
fn number_elements(&self) -> Result<u32> {
let elems = self.len().try_into()?;
Ok(elems)
}
fn cumulative_size(&self) -> Result<u32> {
let mut total: u32 = 0;
for elem in self.iter() {
let elem_bytes = elem.as_bytes_with_nul().len().try_into()?;
total = total.checked_add(elem_bytes).ok_or(Error::Overflow)?;
}
Ok(total)
}
fn write_to_guest<'a>(
&self,
buffer: &GuestPtr<'a, u8>,
element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>,
) -> Result<()> {
let element_heads = element_heads.as_array(self.number_elements()?);
let buffer = buffer.as_array(self.cumulative_size()?);
let mut cursor = 0;
for (elem, head) in self.iter().zip(element_heads.iter()) {
let bytes = elem.as_bytes_with_nul();
let len: u32 = bytes.len().try_into()?;
let elem_buffer = buffer
.get_range(cursor..(cursor + len))
.ok_or(Error::Inval)?; // Elements don't fit in buffer provided
elem_buffer.copy_from_slice(bytes)?;
head?.write(
elem_buffer
.get(0)
.expect("all elem buffers at least length 1"),
)?;
cursor += len;
}
Ok(())
}
}