1mod fdio_sys;
8
9mod spawn_builder;
10
11pub use spawn_builder::{Error as SpawnBuilderError, SpawnBuilder};
12
13use bitflags::bitflags;
14use fidl_fuchsia_io as fio;
15use std::convert::TryInto as _;
16use std::ffi::{CStr, CString, NulError};
17use std::fs::File;
18use std::marker::PhantomData;
19use std::mem::{self, MaybeUninit};
20use std::num::TryFromIntError;
21use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
22use std::os::raw;
23use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
24use std::str::Utf8Error;
25
26pub fn service_connect(service_path: &str, channel: zx::Channel) -> Result<(), zx::Status> {
28 let service_path =
29 CString::new(service_path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
30 let service_path = service_path.as_ptr();
31
32 let channel = channel.into_raw();
34 let status = unsafe { fdio_sys::fdio_service_connect(service_path, channel) };
35 zx::Status::ok(status)
36}
37
38pub fn service_connect_at(
41 dir: &zx::Channel,
42 service_path: &str,
43 channel: zx::Channel,
44) -> Result<(), zx::Status> {
45 let dir = dir.raw_handle();
46 let service_path =
47 CString::new(service_path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
48 let service_path = service_path.as_ptr();
49
50 let channel = channel.into_raw();
52 let status = unsafe { fdio_sys::fdio_service_connect_at(dir, service_path, channel) };
53 zx::Status::ok(status)
54}
55
56pub fn open(path: &str, flags: fio::Flags, channel: zx::Channel) -> Result<(), zx::Status> {
61 let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
62 let path = path.as_ptr();
63 let flags = flags.bits();
64
65 let channel = channel.into_raw();
67 let status = unsafe { fdio_sys::fdio_open3(path, flags, channel) };
68 zx::Status::ok(status)
69}
70
71pub fn open_at(
79 dir: &zx::Channel,
80 path: &str,
81 flags: fio::Flags,
82 channel: zx::Channel,
83) -> Result<(), zx::Status> {
84 let dir = dir.raw_handle();
85 let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
86 let path = path.as_ptr();
87 let flags = flags.bits();
88
89 let channel = channel.into_raw();
91 let status = unsafe { fdio_sys::fdio_open3_at(dir, path, flags, channel) };
92 zx::Status::ok(status)
93}
94
95pub fn open_fd(path: &str, flags: fio::Flags) -> Result<File, zx::Status> {
100 let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
101 let path = path.as_ptr();
102 let flags = flags.bits();
103
104 let mut fd = MaybeUninit::new(-1);
106 let status = {
107 let fd = fd.as_mut_ptr();
108 unsafe { fdio_sys::fdio_open3_fd(path, flags, fd) }
109 };
110 let () = zx::Status::ok(status)?;
111 let fd = unsafe { fd.assume_init() };
112 debug_assert!(fd >= 0, "{} >= 0", fd);
113 let f = unsafe { File::from_raw_fd(fd) };
114 Ok(f)
115}
116
117pub fn open_fd_at(dir: &File, path: &str, flags: fio::Flags) -> Result<File, zx::Status> {
125 let dir = dir.as_raw_fd();
126 let path = CString::new(path).map_err(|NulError { .. }| zx::Status::INVALID_ARGS)?;
127 let path = path.as_ptr();
128 let flags = flags.bits();
129
130 let mut fd = MaybeUninit::new(-1);
132 let status = {
133 let fd = fd.as_mut_ptr();
134 unsafe { fdio_sys::fdio_open3_fd_at(dir, path, flags, fd) }
135 };
136 let () = zx::Status::ok(status)?;
137 let fd = unsafe { fd.assume_init() };
138 debug_assert!(fd >= 0, "{} >= 0", fd);
139 let f = unsafe { File::from_raw_fd(fd) };
140 Ok(f)
141}
142
143pub fn clone_fd(f: impl AsFd) -> Result<zx::NullableHandle, zx::Status> {
145 clone_fd_inner(f.as_fd())
146}
147
148fn clone_fd_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::NullableHandle, zx::Status> {
149 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
151 let status = {
152 let handle = handle.as_mut_ptr();
153 unsafe { fdio_sys::fdio_fd_clone(fd.as_raw_fd(), handle) }
154 };
155 let () = zx::Status::ok(status)?;
156 let handle = unsafe { handle.assume_init() };
157 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
158 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
159 Ok(handle)
160}
161
162pub fn transfer_fd(f: impl Into<OwnedFd>) -> Result<zx::NullableHandle, zx::Status> {
164 transfer_fd_inner(f.into())
165}
166
167fn transfer_fd_inner(fd: OwnedFd) -> Result<zx::NullableHandle, zx::Status> {
168 let fd = fd.into_raw_fd();
169 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
171 let status = {
172 let handle = handle.as_mut_ptr();
173 unsafe { fdio_sys::fdio_fd_transfer(fd.as_raw_fd(), handle) }
174 };
175 let () = zx::Status::ok(status)?;
176 let handle = unsafe { handle.assume_init() };
177 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
178 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
179 Ok(handle)
180}
181
182pub fn create_fd(handle: zx::NullableHandle) -> Result<OwnedFd, zx::Status> {
187 let handle = handle.into_raw();
188 let mut fd = MaybeUninit::new(-1);
190 let status = {
191 let fd = fd.as_mut_ptr();
192 unsafe { fdio_sys::fdio_fd_create(handle, fd) }
193 };
194 let () = zx::Status::ok(status)?;
195 let fd = unsafe { fd.assume_init() };
196 debug_assert!(fd >= 0, "{} >= 0", fd);
197 let f = unsafe { OwnedFd::from_raw_fd(fd) };
199 Ok(f)
200}
201
202pub fn bind_to_fd(handle: zx::NullableHandle, fd: RawFd) -> Result<(), zx::Status> {
207 if fd < 0 {
208 return Err(zx::Status::INVALID_ARGS);
212 }
213
214 let handle = handle.into_raw();
216 let mut fdio = MaybeUninit::new(std::ptr::null_mut());
218
219 let status = unsafe { fdio_sys::fdio_create(handle, fdio.as_mut_ptr()) };
220 let () = zx::Status::ok(status)?;
221 let fdio = unsafe { fdio.assume_init() };
222 debug_assert_ne!(fdio, std::ptr::null_mut());
223 let bound_fd = unsafe { fdio_sys::fdio_bind_to_fd(fdio, fd, 0) };
225 if bound_fd < 0 {
226 return Err(zx::Status::BAD_STATE);
227 }
228 assert_eq!(bound_fd, fd);
230 Ok(())
231}
232
233pub fn clone_channel(f: impl AsFd) -> Result<zx::Channel, zx::Status> {
235 clone_channel_inner(f.as_fd())
236}
237
238fn clone_channel_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::Channel, zx::Status> {
239 let handle = clone_fd(fd)?;
240 let zx::HandleBasicInfo { object_type, .. } = handle.basic_info()?;
241 if object_type == zx::ObjectType::CHANNEL {
242 Ok(handle.into())
243 } else {
244 Err(zx::Status::WRONG_TYPE)
245 }
246}
247
248pub fn pipe_half() -> Result<(File, zx::Socket), zx::Status> {
250 let mut fd = MaybeUninit::new(-1);
252 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
254 let status = {
255 let fd = fd.as_mut_ptr();
256 let handle = handle.as_mut_ptr();
257 unsafe { fdio_sys::fdio_pipe_half(fd, handle) }
258 };
259 let () = zx::Status::ok(status)?;
260 let fd = unsafe { fd.assume_init() };
261 debug_assert!(fd >= 0, "{} >= 0", fd);
262 let f = unsafe { File::from_raw_fd(fd) };
263 let handle = unsafe { handle.assume_init() };
264 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
265 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
266 Ok((f, zx::Socket::from(handle)))
267}
268
269pub fn create_transferrable() -> Result<(File, zx::Channel), zx::Status> {
271 let mut fd = MaybeUninit::new(-1);
273 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
275 let status: i32 =
276 { unsafe { fdio_sys::fdio_transferable_fd(fd.as_mut_ptr(), handle.as_mut_ptr()) } };
277 zx::Status::ok(status)?;
278
279 let fd = unsafe { fd.assume_init() };
280 debug_assert!(fd >= 0, "{} >= 0", fd);
281 let file = unsafe { File::from_raw_fd(fd) };
282 let handle = unsafe { handle.assume_init() };
283 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
284 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
285 Ok((file, zx::Channel::from(handle)))
286}
287
288bitflags! {
289 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
292 pub struct SpawnOptions: u32 {
293 const CLONE_JOB = fdio_sys::FDIO_SPAWN_CLONE_JOB;
298
299 const DEFAULT_LOADER = fdio_sys::FDIO_SPAWN_DEFAULT_LDSVC;
302
303 const CLONE_NAMESPACE = fdio_sys::FDIO_SPAWN_CLONE_NAMESPACE;
305
306 const CLONE_STDIO = fdio_sys::FDIO_SPAWN_CLONE_STDIO;
311
312 const CLONE_ENVIRONMENT = fdio_sys::FDIO_SPAWN_CLONE_ENVIRON;
314
315 const CLONE_ALL = fdio_sys::FDIO_SPAWN_CLONE_ALL;
317 }
318}
319
320fn nul_term_from_slice(argv: &[&CStr]) -> Vec<*const raw::c_char> {
322 argv.iter().map(|cstr| cstr.as_ptr()).chain(std::iter::once(std::ptr::null())).collect()
323}
324
325pub fn spawn(
327 job: &zx::Job,
328 options: SpawnOptions,
329 path: &CStr,
330 argv: &[&CStr],
331) -> Result<zx::Process, zx::Status> {
332 let job = job.raw_handle();
333 let flags = options.bits();
334 let path = path.as_ptr();
335 let argv = nul_term_from_slice(argv);
336 let mut process = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
338
339 let status = {
342 let argv = argv.as_ptr();
343 let process = process.as_mut_ptr();
344 unsafe { fdio_sys::fdio_spawn(job, flags, path, argv, process) }
345 };
346 let () = zx::Status::ok(status)?;
347 let process = unsafe { process.assume_init() };
348 let process = unsafe { zx::NullableHandle::from_raw(process) };
349 debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
350 Ok(zx::Process::from(process))
351}
352
353#[repr(transparent)]
355pub struct SpawnAction<'a>(fdio_sys::fdio_spawn_action_t, PhantomData<&'a ()>);
356
357mod fdio_spawn_action {
360 #![allow(dead_code)]
361 #![allow(non_camel_case_types)]
362
363 pub(super) type action_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1;
364 pub(super) type fd_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_1;
365 pub(super) type ns_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_2;
366 pub(super) type h_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_3;
367 pub(super) type name_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_4;
368 pub(super) type dir_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_5;
369}
370
371impl<'a> SpawnAction<'a> {
372 pub const USE_FOR_STDIO: i32 = fdio_sys::FDIO_FLAG_USE_FOR_STDIO as i32;
373
374 pub fn clone_fd(local_fd: BorrowedFd<'a>, target_fd: i32) -> Self {
379 let local_fd = local_fd.as_fd();
380 Self(
383 fdio_sys::fdio_spawn_action_t {
384 action: fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD,
385 __bindgen_anon_1: fdio_spawn_action::action_t {
386 fd: fdio_spawn_action::fd_t { local_fd: local_fd.as_raw_fd(), target_fd },
387 },
388 },
389 PhantomData,
390 )
391 }
392
393 pub fn transfer_fd(local_fd: OwnedFd, target_fd: i32) -> Self {
398 Self(
401 fdio_sys::fdio_spawn_action_t {
402 action: fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD,
403 __bindgen_anon_1: fdio_spawn_action::action_t {
404 fd: fdio_spawn_action::fd_t { local_fd: local_fd.into_raw_fd(), target_fd },
405 },
406 },
407 PhantomData,
408 )
409 }
410
411 pub fn add_namespace_entry(prefix: &'a CStr, handle: zx::NullableHandle) -> Self {
416 Self(
419 fdio_sys::fdio_spawn_action_t {
420 action: fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
421 __bindgen_anon_1: fdio_spawn_action::action_t {
422 ns: fdio_spawn_action::ns_t {
423 prefix: prefix.as_ptr(),
424 handle: handle.into_raw(),
425 },
426 },
427 },
428 PhantomData,
429 )
430 }
431
432 pub fn add_handle(kind: fuchsia_runtime::HandleInfo, handle: zx::NullableHandle) -> Self {
434 Self(
437 fdio_sys::fdio_spawn_action_t {
438 action: fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE,
439 __bindgen_anon_1: fdio_spawn_action::action_t {
440 h: fdio_spawn_action::h_t { id: kind.as_raw(), handle: handle.into_raw() },
441 },
442 },
443 PhantomData,
444 )
445 }
446
447 pub fn set_name(name: &'a CStr) -> Self {
449 Self(
451 fdio_sys::fdio_spawn_action_t {
452 action: fdio_sys::FDIO_SPAWN_ACTION_SET_NAME,
453 __bindgen_anon_1: fdio_spawn_action::action_t {
454 name: fdio_spawn_action::name_t { data: name.as_ptr() },
455 },
456 },
457 PhantomData,
458 )
459 }
460
461 fn is_null(&self) -> bool {
462 let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
463 *action == 0
464 }
465
466 fn nullify(&mut self) {
468 debug_assert!(
470 (fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD != 0)
471 && (fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD != 0)
472 && (fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY != 0)
473 && (fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE != 0)
474 && (fdio_sys::FDIO_SPAWN_ACTION_SET_NAME != 0)
475 );
476 let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
477 *action = 0;
478 }
479}
480
481const ERR_MSG_MAX_LENGTH: usize = fdio_sys::FDIO_SPAWN_ERR_MSG_MAX_LENGTH as usize;
482
483fn spawn_with_actions(
484 job: &zx::Job,
485 options: SpawnOptions,
486 argv: &[&CStr],
487 environ: Option<&[&CStr]>,
488 actions: &mut [SpawnAction<'_>],
489 spawn_fn: impl FnOnce(
490 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,
499) -> Result<zx::Process, (zx::Status, String)> {
500 let job = job.raw_handle();
501 let flags = options.bits();
502 let argv = nul_term_from_slice(argv);
503 let environ = environ.map(nul_term_from_slice);
504
505 if actions.iter().any(SpawnAction::is_null) {
506 return Err((zx::Status::INVALID_ARGS, "null SpawnAction".to_string()));
507 }
508
509 let mut process = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
511 let mut err_msg = MaybeUninit::new([0; ERR_MSG_MAX_LENGTH]);
512
513 let status = {
514 let environ = environ.as_ref().map_or_else(std::ptr::null, Vec::as_ptr);
515 spawn_fn(
516 job,
517 flags,
518 argv.as_ptr(),
519 environ,
520 actions.len(),
521 actions.as_ptr() as _,
522 process.as_mut_ptr(),
523 err_msg.as_mut_ptr() as _,
524 )
525 };
526
527 std::mem::drop(environ);
530
531 zx::Status::ok(status).map_err(|status| {
532 let err_msg = unsafe { err_msg.assume_init() };
533 let err_msg = unsafe { CStr::from_ptr(err_msg.as_ptr()) };
534 let err_msg = err_msg.to_string_lossy().into_owned();
535 (status, err_msg)
536 })?;
537
538 actions.iter_mut().for_each(SpawnAction::nullify);
540
541 let process = unsafe { process.assume_init() };
542 let process = unsafe { zx::NullableHandle::from_raw(process) };
543 debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
544 Ok(zx::Process::from(process))
545}
546
547pub fn spawn_etc(
550 job: &zx::Job,
551 options: SpawnOptions,
552 path: &CStr,
553 argv: &[&CStr],
554 environ: Option<&[&CStr]>,
555 actions: &mut [SpawnAction<'_>],
556) -> Result<zx::Process, (zx::Status, String)> {
557 let path = path.as_ptr();
558 spawn_with_actions(
559 job,
560 options,
561 argv,
562 environ,
563 actions,
564 |job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
565 fdio_sys::fdio_spawn_etc(
566 job,
567 flags,
568 path,
569 argv,
570 environ,
571 action_count,
572 actions_ptr,
573 process_out,
574 err_msg_out,
575 )
576 },
577 )
578}
579
580pub fn spawn_vmo(
582 job: &zx::Job,
583 options: SpawnOptions,
584 executable_vmo: zx::Vmo,
585 argv: &[&CStr],
586 environ: Option<&[&CStr]>,
587 actions: &mut [SpawnAction<'_>],
588) -> Result<zx::Process, (zx::Status, String)> {
589 let executable_vmo = executable_vmo.into_raw();
590 spawn_with_actions(
591 job,
592 options,
593 argv,
594 environ,
595 actions,
596 |job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
597 fdio_sys::fdio_spawn_vmo(
598 job,
599 flags,
600 executable_vmo,
601 argv,
602 environ,
603 action_count,
604 actions_ptr,
605 process_out,
606 err_msg_out,
607 )
608 },
609 )
610}
611
612pub fn get_vmo_copy_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
616 let fd = f.as_raw_fd();
617 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
619 let status = {
620 let vmo = vmo.as_mut_ptr();
621 unsafe { fdio_sys::fdio_get_vmo_copy(fd, vmo) }
622 };
623 let () = zx::Status::ok(status)?;
624 let vmo = unsafe { vmo.assume_init() };
625 let vmo = unsafe { zx::NullableHandle::from_raw(vmo) };
626 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
627 Ok(zx::Vmo::from(vmo))
628}
629
630pub fn get_vmo_exec_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
632 let fd = f.as_raw_fd();
633 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
635 let status = {
636 let vmo = vmo.as_mut_ptr();
637 unsafe { fdio_sys::fdio_get_vmo_exec(fd, vmo) }
638 };
639 zx::Status::ok(status)?;
640 let vmo = unsafe { zx::NullableHandle::from_raw(vmo.assume_init()) };
641 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
642 Ok(zx::Vmo::from(vmo))
643}
644
645pub fn get_vmo_exact_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
648 let fd = f.as_raw_fd();
649 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
651 let status = {
652 let vmo = vmo.as_mut_ptr();
653 unsafe { fdio_sys::fdio_get_vmo_exact(fd, vmo) }
654 };
655 let () = zx::Status::ok(status)?;
656 let vmo = unsafe { vmo.assume_init() };
657 let vmo = unsafe { zx::NullableHandle::from_raw(vmo) };
658 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
659 Ok(zx::Vmo::from(vmo))
660}
661
662pub struct Namespace {
663 ns: *mut fdio_sys::fdio_ns_t,
664}
665
666impl Namespace {
667 pub fn installed() -> Result<Self, zx::Status> {
669 let mut ns = std::ptr::null_mut();
671 let status = { unsafe { fdio_sys::fdio_ns_get_installed(&mut ns) } };
672 zx::Status::ok(status)?;
673 debug_assert_ne!(ns, std::ptr::null_mut());
674 Ok(Namespace { ns })
675 }
676
677 pub fn open(
683 &self,
684 path: &str,
685 flags: fio::Flags,
686 channel: zx::Channel,
687 ) -> Result<(), zx::Status> {
688 let &Self { ns } = self;
689 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
690 let path = path.as_ptr();
691 let flags = flags.bits();
692
693 let channel = channel.into_raw();
695 let status = unsafe { fdio_sys::fdio_ns_open3(ns, path, flags, channel) };
696 zx::Status::ok(status)
697 }
698
699 pub fn bind(
705 &self,
706 path: &str,
707 channel: fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
708 ) -> Result<(), zx::Status> {
709 let &Self { ns } = self;
710 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
711 let path = path.as_ptr();
712
713 let channel = channel.into_channel().into_raw();
715 let status = unsafe { fdio_sys::fdio_ns_bind(ns, path, channel) };
716 zx::Status::ok(status)
717 }
718
719 pub fn unbind(&self, path: &str) -> Result<(), zx::Status> {
725 let &Self { ns } = self;
726 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
727 let path = path.as_ptr();
728 let status = unsafe { fdio_sys::fdio_ns_unbind(ns, path) };
729 zx::Status::ok(status)
730 }
731
732 pub fn export(&self) -> Result<Vec<NamespaceEntry>, zx::Status> {
735 let mut flat: *mut fdio_sys::fdio_flat_namespace_t = std::ptr::null_mut();
736
737 unsafe {
738 zx::Status::ok(fdio_sys::fdio_ns_export(self.ns, &mut flat))?;
739 let entries = Self::export_entries(flat);
740 fdio_sys::fdio_ns_free_flat_ns(flat);
741 entries
742 }
743 }
744
745 unsafe fn export_entries(
746 flat: *mut fdio_sys::fdio_flat_namespace_t,
747 ) -> Result<Vec<NamespaceEntry>, zx::Status> {
748 let fdio_sys::fdio_flat_namespace_t { count, handle, path } = unsafe { *flat };
749 let len: isize = count.try_into().map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
750 let mut entries = Vec::with_capacity(count);
751 for i in 0..len {
752 let handle = unsafe {
754 zx::NullableHandle::from_raw(mem::replace(
755 &mut *handle.offset(i),
756 zx::sys::ZX_HANDLE_INVALID,
757 ))
758 };
759 entries.push(NamespaceEntry {
760 handle,
761 path: unsafe { CStr::from_ptr(*path.offset(i)) }
762 .to_str()
763 .map_err(|_: Utf8Error| zx::Status::INVALID_ARGS)?
764 .to_owned(),
765 });
766 }
767 Ok(entries)
768 }
769
770 pub fn into_raw(self) -> *mut fdio_sys::fdio_ns_t {
771 let Self { ns } = self;
772 ns
773 }
774}
775
776#[derive(Debug)]
778pub struct NamespaceEntry {
779 pub handle: zx::NullableHandle,
780 pub path: String,
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use assert_matches::assert_matches;
787 use zx::{MonotonicInstant, Signals, Status, object_wait_many};
788
789 #[test]
790 fn namespace_get_installed() {
791 let namespace = Namespace::installed().expect("failed to get installed namespace");
792 assert!(!namespace.into_raw().is_null());
793 }
794
795 #[test]
796 fn namespace_bind_open_unbind() {
797 let namespace = Namespace::installed().unwrap();
798 let (ns_client, _server) = fidl::endpoints::create_endpoints();
803 let (_client, ns_server) = zx::Channel::create();
804 let path = "/test_path1";
805
806 assert_eq!(namespace.bind(path, ns_client), Ok(()));
807 assert_eq!(namespace.open(path, fio::Flags::empty(), ns_server), Ok(()));
808 assert_eq!(namespace.unbind(path), Ok(()));
809 }
810
811 #[test]
812 fn namespace_double_bind_error() {
813 let namespace = Namespace::installed().unwrap();
814 let (ns_client1, _server1) = fidl::endpoints::create_endpoints();
815 let (ns_client2, _server2) = fidl::endpoints::create_endpoints();
816 let path = "/test_path2";
817
818 assert_eq!(namespace.bind(path, ns_client1), Ok(()));
819 assert_eq!(namespace.bind(path, ns_client2), Err(zx::Status::ALREADY_EXISTS));
820 assert_eq!(namespace.unbind(path), Ok(()));
821 }
822
823 #[test]
824 fn namespace_open_error() {
825 let namespace = Namespace::installed().unwrap();
826 let path = "/test_path3";
827
828 let (_client, ns_server) = zx::Channel::create();
829 assert_eq!(
830 namespace.open(path, fio::Flags::empty(), ns_server),
831 Err(zx::Status::NOT_FOUND)
832 );
833 }
834
835 #[test]
836 fn namespace_unbind_error() {
837 let namespace = Namespace::installed().unwrap();
838 let path = "/test_path4";
839
840 assert_eq!(namespace.unbind(path), Err(zx::Status::NOT_FOUND));
841 }
842
843 #[test]
844 fn namespace_export() {
845 let namespace = Namespace::installed().unwrap();
846 let entries = namespace.export().unwrap();
847
848 assert!(!entries.is_empty());
849 }
850
851 fn cstr(orig: &str) -> CString {
852 CString::new(orig).expect("CString::new failed")
853 }
854
855 #[test]
856 fn fdio_spawn_run_target_bin_no_env() {
857 let job = zx::Job::from(zx::NullableHandle::invalid());
858 let cpath = cstr("/pkg/bin/spawn_test_target");
859 let (stdout_file, stdout_sock) = pipe_half().expect("Failed to make pipe");
860 let mut spawn_actions = [SpawnAction::clone_fd(stdout_file.as_fd(), 1)];
861
862 let cstrags: Vec<CString> = vec![cstr("test_arg")];
863 let mut cargs: Vec<&CStr> = cstrags.iter().map(|x| x.as_c_str()).collect();
864 cargs.insert(0, cpath.as_c_str());
865 let process = spawn_etc(
866 &job,
867 SpawnOptions::CLONE_ALL,
868 cpath.as_c_str(),
869 cargs.as_slice(),
870 None,
871 &mut spawn_actions,
872 )
873 .expect("Unable to spawn process");
874
875 let mut output = vec![];
876 loop {
877 let mut items = vec![
878 process.wait_item(Signals::PROCESS_TERMINATED),
879 stdout_sock.wait_item(Signals::SOCKET_READABLE | Signals::SOCKET_PEER_CLOSED),
880 ];
881
882 let signals_result =
883 object_wait_many(&mut items, MonotonicInstant::INFINITE).expect("unable to wait");
884
885 if items[1].pending().contains(Signals::SOCKET_READABLE) {
886 let bytes_len = stdout_sock.outstanding_read_bytes().expect("Socket error");
887 let mut buf: Vec<u8> = vec![0; bytes_len];
888 let read_len = stdout_sock
889 .read(&mut buf[..])
890 .or_else(|status| match status {
891 Status::SHOULD_WAIT => Ok(0),
892 _ => Err(status),
893 })
894 .expect("Unable to read buff");
895 output.extend_from_slice(&buf[0..read_len]);
896 }
897
898 if items[1].pending().contains(Signals::SOCKET_PEER_CLOSED) {
900 break;
901 }
902
903 if items[0].pending().contains(Signals::PROCESS_TERMINATED) {
904 break;
905 }
906
907 if signals_result {
908 break;
909 };
910 }
911
912 assert_eq!(String::from_utf8(output).expect("unable to decode stdout"), "hello world\n");
913 }
914
915 #[test]
919 fn fdio_open_and_open_at() {
920 use rand::distr::SampleString as _;
921
922 {
924 let (_, pkg_server) = zx::Channel::create();
925 assert_eq!(open("pkg", fio::PERM_READABLE, pkg_server), Err(zx::Status::NOT_FOUND));
926 }
927
928 let (pkg_client, pkg_server) = zx::Channel::create();
929 assert_eq!(open("/pkg", fio::PERM_READABLE, pkg_server), Ok(()));
930
931 {
933 let path = rand::distr::Alphanumeric
934 .sample_string(&mut rand::rng(), libc::PATH_MAX.try_into().unwrap());
935 let (_, server) = zx::Channel::create();
936 assert_eq!(
937 open_at(&pkg_client, &path, fio::Flags::empty(), server),
938 Err(zx::Status::INVALID_ARGS)
939 );
940 }
941
942 let (_, bin_server) = zx::Channel::create();
943 assert_eq!(open_at(&pkg_client, "bin", fio::PERM_READABLE, bin_server), Ok(()));
944 }
945
946 #[test]
950 fn fdio_open_fd_and_open_fd_at() {
951 let pkg_fd =
952 open_fd("/pkg", fio::PERM_READABLE).expect("Failed to open /pkg using fdio_open_fd");
953
954 assert_matches!(
956 open_fd_at(&pkg_fd, "blahblah", fio::PERM_READABLE),
957 Err(zx::Status::NOT_FOUND)
958 );
959
960 let _: File = open_fd_at(&pkg_fd, "bin", fio::PERM_READABLE)
961 .expect("Failed to open bin/ subdirectory using fdio_open_fd_at");
962 }
963}