mod fdio_sys;
mod spawn_builder;
pub use spawn_builder::{Error as SpawnBuilderError, SpawnBuilder};
use bitflags::bitflags;
use fidl_fuchsia_io as fio;
use std::convert::TryInto as _;
use std::ffi::{CStr, CString, NulError};
use std::fs::File;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::num::TryFromIntError;
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
use std::os::raw;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::str::Utf8Error;
use zx::{self as zx, AsHandleRef as _, HandleBased as _};
pub fn service_connect(service_path: &str, channel: zx::Channel) -> Result<(), zx::Status> {
let service_path =
CString::new(service_path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let service_path = service_path.as_ptr();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_service_connect(service_path, channel) };
zx::Status::ok(status)
}
pub fn service_connect_at(
dir: &zx::Channel,
service_path: &str,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let dir = dir.raw_handle();
let service_path =
CString::new(service_path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let service_path = service_path.as_ptr();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_service_connect_at(dir, service_path, channel) };
zx::Status::ok(status)
}
pub fn open_deprecated(
path: &str,
flags: fio::OpenFlags,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_open(path, flags, channel) };
zx::Status::ok(status)
}
pub fn open(path: &str, flags: fio::Flags, channel: zx::Channel) -> Result<(), zx::Status> {
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_open3(path, flags, channel) };
zx::Status::ok(status)
}
pub fn open_at_deprecated(
dir: &zx::Channel,
path: &str,
flags: fio::OpenFlags,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let dir = dir.raw_handle();
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_open_at(dir, path, flags, channel) };
zx::Status::ok(status)
}
pub fn open_at(
dir: &zx::Channel,
path: &str,
flags: fio::Flags,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let dir = dir.raw_handle();
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_open3_at(dir, path, flags, channel) };
zx::Status::ok(status)
}
pub fn open_fd_deprecated(path: &str, flags: fio::OpenFlags) -> Result<File, zx::Status> {
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let mut fd = MaybeUninit::new(-1);
let status = {
let fd = fd.as_mut_ptr();
unsafe { fdio_sys::fdio_open_fd(path, flags, fd) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { File::from_raw_fd(fd) };
Ok(f)
}
pub fn open_fd(path: &str, flags: fio::Flags) -> Result<File, zx::Status> {
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let mut fd = MaybeUninit::new(-1);
let status = {
let fd = fd.as_mut_ptr();
unsafe { fdio_sys::fdio_open3_fd(path, flags, fd) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { File::from_raw_fd(fd) };
Ok(f)
}
pub fn open_fd_at_deprecated(
dir: &File,
path: &str,
flags: fio::OpenFlags,
) -> Result<File, zx::Status> {
let dir = dir.as_raw_fd();
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let mut fd = MaybeUninit::new(-1);
let status = {
let fd = fd.as_mut_ptr();
unsafe { fdio_sys::fdio_open_fd_at(dir, path, flags, fd) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { File::from_raw_fd(fd) };
Ok(f)
}
pub fn open_fd_at(dir: &File, path: &str, flags: fio::Flags) -> Result<File, zx::Status> {
let dir = dir.as_raw_fd();
let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
let path = path.as_ptr();
let flags = flags.bits();
let mut fd = MaybeUninit::new(-1);
let status = {
let fd = fd.as_mut_ptr();
unsafe { fdio_sys::fdio_open3_fd_at(dir, path, flags, fd) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { File::from_raw_fd(fd) };
Ok(f)
}
pub fn clone_fd(f: impl AsFd) -> Result<zx::Handle, zx::Status> {
clone_fd_inner(f.as_fd())
}
fn clone_fd_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::Handle, zx::Status> {
let mut handle = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let handle = handle.as_mut_ptr();
unsafe { fdio_sys::fdio_fd_clone(fd.as_raw_fd(), handle) }
};
let () = zx::Status::ok(status)?;
let handle = unsafe { handle.assume_init() };
let handle = unsafe { zx::Handle::from_raw(handle) };
debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
Ok(handle)
}
pub fn transfer_fd(f: impl Into<OwnedFd>) -> Result<zx::Handle, zx::Status> {
transfer_fd_inner(f.into())
}
fn transfer_fd_inner(fd: OwnedFd) -> Result<zx::Handle, zx::Status> {
let fd = fd.into_raw_fd();
let mut handle = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let handle = handle.as_mut_ptr();
unsafe { fdio_sys::fdio_fd_transfer(fd.as_raw_fd(), handle) }
};
let () = zx::Status::ok(status)?;
let handle = unsafe { handle.assume_init() };
let handle = unsafe { zx::Handle::from_raw(handle) };
debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
Ok(handle)
}
pub fn create_fd(handle: zx::Handle) -> Result<OwnedFd, zx::Status> {
let handle = handle.into_raw();
let mut fd = MaybeUninit::new(-1);
let status = {
let fd = fd.as_mut_ptr();
unsafe { fdio_sys::fdio_fd_create(handle, fd) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(f)
}
pub fn bind_to_fd(handle: zx::Handle, fd: RawFd) -> Result<(), zx::Status> {
if fd < 0 {
return Err(zx::Status::INVALID_ARGS);
}
let handle = handle.into_raw();
let mut fdio = MaybeUninit::new(std::ptr::null_mut());
let status = unsafe { fdio_sys::fdio_create(handle, fdio.as_mut_ptr()) };
let () = zx::Status::ok(status)?;
let fdio = unsafe { fdio.assume_init() };
debug_assert_ne!(fdio, std::ptr::null_mut());
let bound_fd = unsafe { fdio_sys::fdio_bind_to_fd(fdio, fd, 0) };
if bound_fd < 0 {
return Err(zx::Status::BAD_STATE);
}
assert_eq!(bound_fd, fd);
Ok(())
}
pub fn clone_channel(f: impl AsFd) -> Result<zx::Channel, zx::Status> {
clone_channel_inner(f.as_fd())
}
fn clone_channel_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::Channel, zx::Status> {
let handle = clone_fd(fd)?;
let zx::HandleBasicInfo { object_type, .. } = handle.basic_info()?;
if object_type == zx::ObjectType::CHANNEL {
Ok(handle.into())
} else {
Err(zx::Status::WRONG_TYPE)
}
}
pub fn pipe_half() -> Result<(File, zx::Socket), zx::Status> {
let mut fd = MaybeUninit::new(-1);
let mut handle = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let fd = fd.as_mut_ptr();
let handle = handle.as_mut_ptr();
unsafe { fdio_sys::fdio_pipe_half(fd, handle) }
};
let () = zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let f = unsafe { File::from_raw_fd(fd) };
let handle = unsafe { handle.assume_init() };
let handle = unsafe { zx::Handle::from_raw(handle) };
debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
Ok((f, zx::Socket::from(handle)))
}
pub fn create_transferrable() -> Result<(File, zx::Channel), zx::Status> {
let mut fd = MaybeUninit::new(-1);
let mut handle = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status: i32 =
{ unsafe { fdio_sys::fdio_transferable_fd(fd.as_mut_ptr(), handle.as_mut_ptr()) } };
zx::Status::ok(status)?;
let fd = unsafe { fd.assume_init() };
debug_assert!(fd >= 0, "{} >= 0", fd);
let file = unsafe { File::from_raw_fd(fd) };
let handle = unsafe { handle.assume_init() };
let handle = unsafe { zx::Handle::from_raw(handle) };
debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
Ok((file, zx::Channel::from(handle)))
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SpawnOptions: u32 {
const CLONE_JOB = fdio_sys::FDIO_SPAWN_CLONE_JOB;
const DEFAULT_LOADER = fdio_sys::FDIO_SPAWN_DEFAULT_LDSVC;
const CLONE_NAMESPACE = fdio_sys::FDIO_SPAWN_CLONE_NAMESPACE;
const CLONE_STDIO = fdio_sys::FDIO_SPAWN_CLONE_STDIO;
const CLONE_ENVIRONMENT = fdio_sys::FDIO_SPAWN_CLONE_ENVIRON;
const CLONE_ALL = fdio_sys::FDIO_SPAWN_CLONE_ALL;
}
}
fn nul_term_from_slice(argv: &[&CStr]) -> Vec<*const raw::c_char> {
argv.iter().map(|cstr| cstr.as_ptr()).chain(std::iter::once(std::ptr::null())).collect()
}
pub fn spawn(
job: &zx::Job,
options: SpawnOptions,
path: &CStr,
argv: &[&CStr],
) -> Result<zx::Process, zx::Status> {
let job = job.raw_handle();
let flags = options.bits();
let path = path.as_ptr();
let argv = nul_term_from_slice(argv);
let mut process = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let argv = argv.as_ptr();
let process = process.as_mut_ptr();
unsafe { fdio_sys::fdio_spawn(job, flags, path, argv, process) }
};
let () = zx::Status::ok(status)?;
let process = unsafe { process.assume_init() };
let process = unsafe { zx::Handle::from_raw(process) };
debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
Ok(zx::Process::from(process))
}
#[repr(transparent)]
pub struct SpawnAction<'a>(fdio_sys::fdio_spawn_action_t, PhantomData<&'a ()>);
mod fdio_spawn_action {
#![allow(dead_code)]
#![allow(non_camel_case_types)]
pub(super) type action_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1;
pub(super) type fd_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_1;
pub(super) type ns_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_2;
pub(super) type h_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_3;
pub(super) type name_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_4;
pub(super) type dir_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_5;
}
impl<'a> SpawnAction<'a> {
pub const USE_FOR_STDIO: i32 = fdio_sys::FDIO_FLAG_USE_FOR_STDIO as i32;
pub fn clone_fd(local_fd: BorrowedFd<'a>, target_fd: i32) -> Self {
let local_fd = local_fd.as_fd();
Self(
fdio_sys::fdio_spawn_action_t {
action: fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD,
__bindgen_anon_1: fdio_spawn_action::action_t {
fd: fdio_spawn_action::fd_t { local_fd: local_fd.as_raw_fd(), target_fd },
},
},
PhantomData,
)
}
pub fn transfer_fd(local_fd: OwnedFd, target_fd: i32) -> Self {
Self(
fdio_sys::fdio_spawn_action_t {
action: fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD,
__bindgen_anon_1: fdio_spawn_action::action_t {
fd: fdio_spawn_action::fd_t { local_fd: local_fd.into_raw_fd(), target_fd },
},
},
PhantomData,
)
}
pub fn add_namespace_entry(prefix: &'a CStr, handle: zx::Handle) -> Self {
Self(
fdio_sys::fdio_spawn_action_t {
action: fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
__bindgen_anon_1: fdio_spawn_action::action_t {
ns: fdio_spawn_action::ns_t {
prefix: prefix.as_ptr(),
handle: handle.into_raw(),
},
},
},
PhantomData,
)
}
pub fn add_handle(kind: fuchsia_runtime::HandleInfo, handle: zx::Handle) -> Self {
Self(
fdio_sys::fdio_spawn_action_t {
action: fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE,
__bindgen_anon_1: fdio_spawn_action::action_t {
h: fdio_spawn_action::h_t { id: kind.as_raw(), handle: handle.into_raw() },
},
},
PhantomData,
)
}
pub fn set_name(name: &'a CStr) -> Self {
Self(
fdio_sys::fdio_spawn_action_t {
action: fdio_sys::FDIO_SPAWN_ACTION_SET_NAME,
__bindgen_anon_1: fdio_spawn_action::action_t {
name: fdio_spawn_action::name_t { data: name.as_ptr() },
},
},
PhantomData,
)
}
fn is_null(&self) -> bool {
let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
*action == 0
}
fn nullify(&mut self) {
debug_assert!(
(fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD != 0)
&& (fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD != 0)
&& (fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY != 0)
&& (fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE != 0)
&& (fdio_sys::FDIO_SPAWN_ACTION_SET_NAME != 0)
);
let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
*action = 0;
}
}
const ERR_MSG_MAX_LENGTH: usize = fdio_sys::FDIO_SPAWN_ERR_MSG_MAX_LENGTH as usize;
fn spawn_with_actions(
job: &zx::Job,
options: SpawnOptions,
argv: &[&CStr],
environ: Option<&[&CStr]>,
actions: &mut [SpawnAction<'_>],
spawn_fn: impl FnOnce(
zx::sys::zx_handle_t, u32, *const *const raw::c_char, *const *const raw::c_char, usize, *const fdio_sys::fdio_spawn_action_t, *mut zx::sys::zx_handle_t, *mut raw::c_char, ) -> zx::sys::zx_status_t,
) -> Result<zx::Process, (zx::Status, String)> {
let job = job.raw_handle();
let flags = options.bits();
let argv = nul_term_from_slice(argv);
let environ = environ.map(nul_term_from_slice);
if actions.iter().any(SpawnAction::is_null) {
return Err((zx::Status::INVALID_ARGS, "null SpawnAction".to_string()));
}
let mut process = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let mut err_msg = MaybeUninit::new([0; ERR_MSG_MAX_LENGTH]);
let status = {
let environ = environ.as_ref().map_or_else(std::ptr::null, Vec::as_ptr);
spawn_fn(
job,
flags,
argv.as_ptr(),
environ,
actions.len(),
actions.as_ptr() as _,
process.as_mut_ptr(),
err_msg.as_mut_ptr() as _,
)
};
std::mem::drop(environ);
zx::Status::ok(status).map_err(|status| {
let err_msg = unsafe { err_msg.assume_init() };
let err_msg = unsafe { CStr::from_ptr(err_msg.as_ptr()) };
let err_msg = err_msg.to_string_lossy().into_owned();
(status, err_msg)
})?;
actions.iter_mut().for_each(SpawnAction::nullify);
let process = unsafe { process.assume_init() };
let process = unsafe { zx::Handle::from_raw(process) };
debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
Ok(zx::Process::from(process))
}
pub fn spawn_etc(
job: &zx::Job,
options: SpawnOptions,
path: &CStr,
argv: &[&CStr],
environ: Option<&[&CStr]>,
actions: &mut [SpawnAction<'_>],
) -> Result<zx::Process, (zx::Status, String)> {
let path = path.as_ptr();
spawn_with_actions(
job,
options,
argv,
environ,
actions,
|job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
fdio_sys::fdio_spawn_etc(
job,
flags,
path,
argv,
environ,
action_count,
actions_ptr,
process_out,
err_msg_out,
)
},
)
}
pub fn spawn_vmo(
job: &zx::Job,
options: SpawnOptions,
executable_vmo: zx::Vmo,
argv: &[&CStr],
environ: Option<&[&CStr]>,
actions: &mut [SpawnAction<'_>],
) -> Result<zx::Process, (zx::Status, String)> {
let executable_vmo = executable_vmo.into_raw();
spawn_with_actions(
job,
options,
argv,
environ,
actions,
|job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
fdio_sys::fdio_spawn_vmo(
job,
flags,
executable_vmo,
argv,
environ,
action_count,
actions_ptr,
process_out,
err_msg_out,
)
},
)
}
pub fn get_vmo_copy_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
let fd = f.as_raw_fd();
let mut vmo = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let vmo = vmo.as_mut_ptr();
unsafe { fdio_sys::fdio_get_vmo_copy(fd, vmo) }
};
let () = zx::Status::ok(status)?;
let vmo = unsafe { vmo.assume_init() };
let vmo = unsafe { zx::Handle::from_raw(vmo) };
debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
Ok(zx::Vmo::from(vmo))
}
pub fn get_vmo_exec_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
let fd = f.as_raw_fd();
let mut vmo = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let vmo = vmo.as_mut_ptr();
unsafe { fdio_sys::fdio_get_vmo_exec(fd, vmo) }
};
zx::Status::ok(status)?;
let vmo = unsafe { zx::Handle::from_raw(vmo.assume_init()) };
debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
Ok(zx::Vmo::from(vmo))
}
pub fn get_vmo_exact_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
let fd = f.as_raw_fd();
let mut vmo = MaybeUninit::new(zx::Handle::invalid().raw_handle());
let status = {
let vmo = vmo.as_mut_ptr();
unsafe { fdio_sys::fdio_get_vmo_exact(fd, vmo) }
};
let () = zx::Status::ok(status)?;
let vmo = unsafe { vmo.assume_init() };
let vmo = unsafe { zx::Handle::from_raw(vmo) };
debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
Ok(zx::Vmo::from(vmo))
}
pub struct Namespace {
ns: *mut fdio_sys::fdio_ns_t,
}
impl Namespace {
pub fn installed() -> Result<Self, zx::Status> {
let mut ns = std::ptr::null_mut();
let status = { unsafe { fdio_sys::fdio_ns_get_installed(&mut ns) } };
zx::Status::ok(status)?;
debug_assert_ne!(ns, std::ptr::null_mut());
Ok(Namespace { ns })
}
pub fn open_deprecated(
&self,
path: &str,
flags: fio::OpenFlags,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let &Self { ns } = self;
let path = CString::new(path)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_ns_open(ns, path, flags, channel) };
zx::Status::ok(status)
}
pub fn open(
&self,
path: &str,
flags: fio::Flags,
channel: zx::Channel,
) -> Result<(), zx::Status> {
let &Self { ns } = self;
let path = CString::new(path)?;
let path = path.as_ptr();
let flags = flags.bits();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_ns_open3(ns, path, flags, channel) };
zx::Status::ok(status)
}
pub fn bind(
&self,
path: &str,
channel: fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
) -> Result<(), zx::Status> {
let &Self { ns } = self;
let path = CString::new(path)?;
let path = path.as_ptr();
let channel = channel.into_raw();
let status = unsafe { fdio_sys::fdio_ns_bind(ns, path, channel) };
zx::Status::ok(status)
}
pub fn unbind(&self, path: &str) -> Result<(), zx::Status> {
let &Self { ns } = self;
let path = CString::new(path)?;
let path = path.as_ptr();
let status = unsafe { fdio_sys::fdio_ns_unbind(ns, path) };
zx::Status::ok(status)
}
pub fn export(&self) -> Result<Vec<NamespaceEntry>, zx::Status> {
let mut flat: *mut fdio_sys::fdio_flat_namespace_t = std::ptr::null_mut();
unsafe {
zx::Status::ok(fdio_sys::fdio_ns_export(self.ns, &mut flat))?;
let entries = Self::export_entries(flat);
fdio_sys::fdio_ns_free_flat_ns(flat);
entries
}
}
unsafe fn export_entries(
flat: *mut fdio_sys::fdio_flat_namespace_t,
) -> Result<Vec<NamespaceEntry>, zx::Status> {
let fdio_sys::fdio_flat_namespace_t { count, handle, path } = *flat;
let len: isize = count.try_into().map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
let mut entries = Vec::with_capacity(count);
for i in 0..len {
let handle = zx::Handle::from_raw(mem::replace(
&mut *handle.offset(i),
zx::sys::ZX_HANDLE_INVALID,
));
entries.push(NamespaceEntry {
handle,
path: CStr::from_ptr(*path.offset(i))
.to_str()
.map_err(|_: Utf8Error| zx::Status::INVALID_ARGS)?
.to_owned(),
});
}
Ok(entries)
}
pub fn into_raw(self) -> *mut fdio_sys::fdio_ns_t {
let Self { ns } = self;
ns
}
}
#[derive(Debug)]
pub struct NamespaceEntry {
pub handle: zx::Handle,
pub path: String,
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use zx::{object_wait_many, MonotonicInstant, Signals, Status, WaitItem};
#[test]
fn namespace_get_installed() {
let namespace = Namespace::installed().expect("failed to get installed namespace");
assert!(!namespace.into_raw().is_null());
}
#[test]
fn namespace_bind_open_unbind() {
let namespace = Namespace::installed().unwrap();
let (ns_client, _server) = fidl::endpoints::create_endpoints();
let (_client, ns_server) = zx::Channel::create();
let path = "/test_path1";
assert_eq!(namespace.bind(path, ns_client), Ok(()));
assert_eq!(namespace.open_deprecated(path, fio::OpenFlags::empty(), ns_server), Ok(()));
assert_eq!(namespace.unbind(path), Ok(()));
}
#[test]
fn namespace_bind_open3_unbind() {
let namespace = Namespace::installed().unwrap();
let (ns_client, _server) = fidl::endpoints::create_endpoints();
let (_client, ns_server) = zx::Channel::create();
let path = "/test_path1";
assert_eq!(namespace.bind(path, ns_client), Ok(()));
assert_eq!(namespace.open(path, fio::Flags::empty(), ns_server), Ok(()));
assert_eq!(namespace.unbind(path), Ok(()));
}
#[test]
fn namespace_double_bind_error() {
let namespace = Namespace::installed().unwrap();
let (ns_client1, _server1) = fidl::endpoints::create_endpoints();
let (ns_client2, _server2) = fidl::endpoints::create_endpoints();
let path = "/test_path2";
assert_eq!(namespace.bind(path, ns_client1), Ok(()));
assert_eq!(namespace.bind(path, ns_client2), Err(zx::Status::ALREADY_EXISTS));
assert_eq!(namespace.unbind(path), Ok(()));
}
#[test]
fn namespace_open_error() {
let namespace = Namespace::installed().unwrap();
let (_client, ns_server) = zx::Channel::create();
let path = "/test_path3";
assert_eq!(
namespace.open_deprecated(path, fio::OpenFlags::empty(), ns_server),
Err(zx::Status::NOT_FOUND)
);
let (_client, ns_server) = zx::Channel::create();
assert_eq!(
namespace.open(path, fio::Flags::empty(), ns_server),
Err(zx::Status::NOT_FOUND)
);
}
#[test]
fn namespace_unbind_error() {
let namespace = Namespace::installed().unwrap();
let path = "/test_path4";
assert_eq!(namespace.unbind(path), Err(zx::Status::NOT_FOUND));
}
#[test]
fn namespace_export() {
let namespace = Namespace::installed().unwrap();
let entries = namespace.export().unwrap();
assert!(!entries.is_empty());
}
fn cstr(orig: &str) -> CString {
CString::new(orig).expect("CString::new failed")
}
#[test]
fn fdio_spawn_run_target_bin_no_env() {
let job = zx::Job::from(zx::Handle::invalid());
let cpath = cstr("/pkg/bin/spawn_test_target");
let (stdout_file, stdout_sock) = pipe_half().expect("Failed to make pipe");
let mut spawn_actions = [SpawnAction::clone_fd(stdout_file.as_fd(), 1)];
let cstrags: Vec<CString> = vec![cstr("test_arg")];
let mut cargs: Vec<&CStr> = cstrags.iter().map(|x| x.as_c_str()).collect();
cargs.insert(0, cpath.as_c_str());
let process = spawn_etc(
&job,
SpawnOptions::CLONE_ALL,
cpath.as_c_str(),
cargs.as_slice(),
None,
&mut spawn_actions,
)
.expect("Unable to spawn process");
let mut output = vec![];
loop {
let mut items = vec![
WaitItem {
handle: process.as_handle_ref(),
waitfor: Signals::PROCESS_TERMINATED,
pending: Signals::NONE,
},
WaitItem {
handle: stdout_sock.as_handle_ref(),
waitfor: Signals::SOCKET_READABLE | Signals::SOCKET_PEER_CLOSED,
pending: Signals::NONE,
},
];
let signals_result =
object_wait_many(&mut items, MonotonicInstant::INFINITE).expect("unable to wait");
if items[1].pending.contains(Signals::SOCKET_READABLE) {
let bytes_len = stdout_sock.outstanding_read_bytes().expect("Socket error");
let mut buf: Vec<u8> = vec![0; bytes_len];
let read_len = stdout_sock
.read(&mut buf[..])
.or_else(|status| match status {
Status::SHOULD_WAIT => Ok(0),
_ => Err(status),
})
.expect("Unable to read buff");
output.extend_from_slice(&buf[0..read_len]);
}
if items[1].pending.contains(Signals::SOCKET_PEER_CLOSED) {
break;
}
if items[0].pending.contains(Signals::PROCESS_TERMINATED) {
break;
}
if signals_result {
break;
};
}
assert_eq!(String::from_utf8(output).expect("unable to decode stdout"), "hello world\n");
}
#[test]
fn fdio_open_and_open_at() {
use rand::distributions::DistString as _;
{
let (_, pkg_server) = zx::Channel::create();
assert_eq!(
open_deprecated("pkg", fio::OpenFlags::RIGHT_READABLE, pkg_server),
Err(zx::Status::NOT_FOUND)
);
}
let (pkg_client, pkg_server) = zx::Channel::create();
assert_eq!(open_deprecated("/pkg", fio::OpenFlags::RIGHT_READABLE, pkg_server), Ok(()));
{
let path = rand::distributions::Alphanumeric
.sample_string(&mut rand::thread_rng(), libc::PATH_MAX.try_into().unwrap());
let (_, server) = zx::Channel::create();
assert_eq!(
open_at_deprecated(&pkg_client, &path, fio::OpenFlags::empty(), server),
Err(zx::Status::INVALID_ARGS)
);
}
let (_, bin_server) = zx::Channel::create();
assert_eq!(
open_at_deprecated(&pkg_client, "bin", fio::OpenFlags::RIGHT_READABLE, bin_server),
Ok(())
);
}
#[test]
fn fdio_open_fd_and_open_fd_at_deprecated() {
let pkg_fd = open_fd_deprecated("/pkg", fio::OpenFlags::RIGHT_READABLE)
.expect("Failed to open /pkg using fdio_open_fd");
assert_matches!(
open_fd_at_deprecated(&pkg_fd, "blahblah", fio::OpenFlags::RIGHT_READABLE),
Err(zx::Status::NOT_FOUND)
);
let _: File = open_fd_at_deprecated(&pkg_fd, "bin", fio::OpenFlags::RIGHT_READABLE)
.expect("Failed to open bin/ subdirectory using fdio_open_fd_at");
}
}