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