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 create_fd_null() -> Option<OwnedFd> {
206 let fd = unsafe { fdio_sys::fdio_fd_create_null() };
207 if fd < 0 {
208 return None;
209 }
210 let f = unsafe { OwnedFd::from_raw_fd(fd) };
212 Some(f)
213}
214
215pub fn bind_to_fd(handle: zx::NullableHandle, fd: RawFd) -> Result<(), zx::Status> {
220 if fd < 0 {
221 return Err(zx::Status::INVALID_ARGS);
225 }
226
227 let handle = handle.into_raw();
229 let mut fdio = MaybeUninit::new(std::ptr::null_mut());
231
232 let status = unsafe { fdio_sys::fdio_create(handle, fdio.as_mut_ptr()) };
233 let () = zx::Status::ok(status)?;
234 let fdio = unsafe { fdio.assume_init() };
235 debug_assert_ne!(fdio, std::ptr::null_mut());
236 let bound_fd = unsafe { fdio_sys::fdio_bind_to_fd(fdio, fd, 0) };
238 if bound_fd < 0 {
239 return Err(zx::Status::BAD_STATE);
240 }
241 assert_eq!(bound_fd, fd);
243 Ok(())
244}
245
246pub fn clone_channel(f: impl AsFd) -> Result<zx::Channel, zx::Status> {
248 clone_channel_inner(f.as_fd())
249}
250
251fn clone_channel_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::Channel, zx::Status> {
252 let handle = clone_fd(fd)?;
253 let zx::HandleBasicInfo { object_type, .. } = handle.basic_info()?;
254 if object_type == zx::ObjectType::CHANNEL {
255 Ok(handle.into())
256 } else {
257 Err(zx::Status::WRONG_TYPE)
258 }
259}
260
261pub fn pipe_half() -> Result<(File, zx::Socket), zx::Status> {
263 let mut fd = MaybeUninit::new(-1);
265 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
267 let status = {
268 let fd = fd.as_mut_ptr();
269 let handle = handle.as_mut_ptr();
270 unsafe { fdio_sys::fdio_pipe_half(fd, handle) }
271 };
272 let () = zx::Status::ok(status)?;
273 let fd = unsafe { fd.assume_init() };
274 debug_assert!(fd >= 0, "{} >= 0", fd);
275 let f = unsafe { File::from_raw_fd(fd) };
276 let handle = unsafe { handle.assume_init() };
277 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
278 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
279 Ok((f, zx::Socket::from(handle)))
280}
281
282pub fn create_transferrable() -> Result<(File, zx::Channel), zx::Status> {
284 let mut fd = MaybeUninit::new(-1);
286 let mut handle = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
288 let status: i32 =
289 { unsafe { fdio_sys::fdio_transferable_fd(fd.as_mut_ptr(), handle.as_mut_ptr()) } };
290 zx::Status::ok(status)?;
291
292 let fd = unsafe { fd.assume_init() };
293 debug_assert!(fd >= 0, "{} >= 0", fd);
294 let file = unsafe { File::from_raw_fd(fd) };
295 let handle = unsafe { handle.assume_init() };
296 let handle = unsafe { zx::NullableHandle::from_raw(handle) };
297 debug_assert!(!handle.is_invalid(), "({:?}).is_invalid()", handle);
298 Ok((file, zx::Channel::from(handle)))
299}
300
301bitflags! {
302 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
305 pub struct SpawnOptions: u32 {
306 const CLONE_JOB = fdio_sys::FDIO_SPAWN_CLONE_JOB;
311
312 const DEFAULT_LOADER = fdio_sys::FDIO_SPAWN_DEFAULT_LDSVC;
315
316 const CLONE_NAMESPACE = fdio_sys::FDIO_SPAWN_CLONE_NAMESPACE;
318
319 const CLONE_STDIO = fdio_sys::FDIO_SPAWN_CLONE_STDIO;
324
325 const CLONE_ENVIRONMENT = fdio_sys::FDIO_SPAWN_CLONE_ENVIRON;
327
328 const CLONE_ALL = fdio_sys::FDIO_SPAWN_CLONE_ALL;
330 }
331}
332
333fn nul_term_from_slice(argv: &[&CStr]) -> Vec<*const raw::c_char> {
335 argv.iter().map(|cstr| cstr.as_ptr()).chain(std::iter::once(std::ptr::null())).collect()
336}
337
338pub fn spawn(
340 job: &zx::Job,
341 options: SpawnOptions,
342 path: &CStr,
343 argv: &[&CStr],
344) -> Result<zx::Process, zx::Status> {
345 let job = job.raw_handle();
346 let flags = options.bits();
347 let path = path.as_ptr();
348 let argv = nul_term_from_slice(argv);
349 let mut process = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
351
352 let status = {
355 let argv = argv.as_ptr();
356 let process = process.as_mut_ptr();
357 unsafe { fdio_sys::fdio_spawn(job, flags, path, argv, process) }
358 };
359 let () = zx::Status::ok(status)?;
360 let process = unsafe { process.assume_init() };
361 let process = unsafe { zx::NullableHandle::from_raw(process) };
362 debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
363 Ok(zx::Process::from(process))
364}
365
366#[repr(transparent)]
368pub struct SpawnAction<'a>(fdio_sys::fdio_spawn_action_t, PhantomData<&'a ()>);
369
370mod fdio_spawn_action {
373 #![allow(dead_code)]
374 #![allow(non_camel_case_types)]
375
376 pub(super) type action_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1;
377 pub(super) type fd_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_1;
378 pub(super) type ns_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_2;
379 pub(super) type h_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_3;
380 pub(super) type name_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_4;
381 pub(super) type dir_t = super::fdio_sys::fdio_spawn_action__bindgen_ty_1__bindgen_ty_5;
382}
383
384impl<'a> SpawnAction<'a> {
385 pub const USE_FOR_STDIO: i32 = fdio_sys::FDIO_FLAG_USE_FOR_STDIO as i32;
386
387 pub fn clone_fd(local_fd: BorrowedFd<'a>, target_fd: i32) -> Self {
392 let local_fd = local_fd.as_fd();
393 Self(
396 fdio_sys::fdio_spawn_action_t {
397 action: fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD,
398 __bindgen_anon_1: fdio_spawn_action::action_t {
399 fd: fdio_spawn_action::fd_t { local_fd: local_fd.as_raw_fd(), target_fd },
400 },
401 },
402 PhantomData,
403 )
404 }
405
406 pub fn transfer_fd(local_fd: OwnedFd, target_fd: i32) -> Self {
411 Self(
414 fdio_sys::fdio_spawn_action_t {
415 action: fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD,
416 __bindgen_anon_1: fdio_spawn_action::action_t {
417 fd: fdio_spawn_action::fd_t { local_fd: local_fd.into_raw_fd(), target_fd },
418 },
419 },
420 PhantomData,
421 )
422 }
423
424 pub fn add_namespace_entry(prefix: &'a CStr, handle: zx::NullableHandle) -> Self {
429 Self(
432 fdio_sys::fdio_spawn_action_t {
433 action: fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
434 __bindgen_anon_1: fdio_spawn_action::action_t {
435 ns: fdio_spawn_action::ns_t {
436 prefix: prefix.as_ptr(),
437 handle: handle.into_raw(),
438 },
439 },
440 },
441 PhantomData,
442 )
443 }
444
445 pub fn add_handle(kind: fuchsia_runtime::HandleInfo, handle: zx::NullableHandle) -> Self {
447 Self(
450 fdio_sys::fdio_spawn_action_t {
451 action: fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE,
452 __bindgen_anon_1: fdio_spawn_action::action_t {
453 h: fdio_spawn_action::h_t { id: kind.as_raw(), handle: handle.into_raw() },
454 },
455 },
456 PhantomData,
457 )
458 }
459
460 pub fn set_name(name: &'a CStr) -> Self {
462 Self(
464 fdio_sys::fdio_spawn_action_t {
465 action: fdio_sys::FDIO_SPAWN_ACTION_SET_NAME,
466 __bindgen_anon_1: fdio_spawn_action::action_t {
467 name: fdio_spawn_action::name_t { data: name.as_ptr() },
468 },
469 },
470 PhantomData,
471 )
472 }
473
474 fn is_null(&self) -> bool {
475 let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
476 *action == 0
477 }
478
479 fn nullify(&mut self) {
481 debug_assert!(
483 (fdio_sys::FDIO_SPAWN_ACTION_CLONE_FD != 0)
484 && (fdio_sys::FDIO_SPAWN_ACTION_TRANSFER_FD != 0)
485 && (fdio_sys::FDIO_SPAWN_ACTION_ADD_NS_ENTRY != 0)
486 && (fdio_sys::FDIO_SPAWN_ACTION_ADD_HANDLE != 0)
487 && (fdio_sys::FDIO_SPAWN_ACTION_SET_NAME != 0)
488 );
489 let Self(fdio_sys::fdio_spawn_action_t { action, __bindgen_anon_1: _ }, PhantomData) = self;
490 *action = 0;
491 }
492}
493
494const ERR_MSG_MAX_LENGTH: usize = fdio_sys::FDIO_SPAWN_ERR_MSG_MAX_LENGTH as usize;
495
496fn spawn_with_actions(
497 job: &zx::Job,
498 options: SpawnOptions,
499 argv: &[&CStr],
500 environ: Option<&[&CStr]>,
501 actions: &mut [SpawnAction<'_>],
502 spawn_fn: impl FnOnce(
503 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,
512) -> Result<zx::Process, (zx::Status, String)> {
513 let job = job.raw_handle();
514 let flags = options.bits();
515 let argv = nul_term_from_slice(argv);
516 let environ = environ.map(nul_term_from_slice);
517
518 if actions.iter().any(SpawnAction::is_null) {
519 return Err((zx::Status::INVALID_ARGS, "null SpawnAction".to_string()));
520 }
521
522 let mut process = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
524 let mut err_msg = MaybeUninit::new([0; ERR_MSG_MAX_LENGTH]);
525
526 let status = {
527 let environ = environ.as_ref().map_or_else(std::ptr::null, Vec::as_ptr);
528 spawn_fn(
529 job,
530 flags,
531 argv.as_ptr(),
532 environ,
533 actions.len(),
534 actions.as_ptr() as _,
535 process.as_mut_ptr(),
536 err_msg.as_mut_ptr() as _,
537 )
538 };
539
540 std::mem::drop(environ);
543
544 zx::Status::ok(status).map_err(|status| {
545 let err_msg = unsafe { err_msg.assume_init() };
546 let err_msg = unsafe { CStr::from_ptr(err_msg.as_ptr()) };
547 let err_msg = err_msg.to_string_lossy().into_owned();
548 (status, err_msg)
549 })?;
550
551 actions.iter_mut().for_each(SpawnAction::nullify);
553
554 let process = unsafe { process.assume_init() };
555 let process = unsafe { zx::NullableHandle::from_raw(process) };
556 debug_assert!(!process.is_invalid(), "({:?}).is_invalid()", process);
557 Ok(zx::Process::from(process))
558}
559
560pub fn spawn_etc(
563 job: &zx::Job,
564 options: SpawnOptions,
565 path: &CStr,
566 argv: &[&CStr],
567 environ: Option<&[&CStr]>,
568 actions: &mut [SpawnAction<'_>],
569) -> Result<zx::Process, (zx::Status, String)> {
570 let path = path.as_ptr();
571 spawn_with_actions(
572 job,
573 options,
574 argv,
575 environ,
576 actions,
577 |job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
578 fdio_sys::fdio_spawn_etc(
579 job,
580 flags,
581 path,
582 argv,
583 environ,
584 action_count,
585 actions_ptr,
586 process_out,
587 err_msg_out,
588 )
589 },
590 )
591}
592
593pub fn spawn_vmo(
595 job: &zx::Job,
596 options: SpawnOptions,
597 executable_vmo: zx::Vmo,
598 argv: &[&CStr],
599 environ: Option<&[&CStr]>,
600 actions: &mut [SpawnAction<'_>],
601) -> Result<zx::Process, (zx::Status, String)> {
602 let executable_vmo = executable_vmo.into_raw();
603 spawn_with_actions(
604 job,
605 options,
606 argv,
607 environ,
608 actions,
609 |job, flags, argv, environ, action_count, actions_ptr, process_out, err_msg_out| unsafe {
610 fdio_sys::fdio_spawn_vmo(
611 job,
612 flags,
613 executable_vmo,
614 argv,
615 environ,
616 action_count,
617 actions_ptr,
618 process_out,
619 err_msg_out,
620 )
621 },
622 )
623}
624
625pub fn get_vmo_copy_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
629 let fd = f.as_raw_fd();
630 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
632 let status = {
633 let vmo = vmo.as_mut_ptr();
634 unsafe { fdio_sys::fdio_get_vmo_copy(fd, vmo) }
635 };
636 let () = zx::Status::ok(status)?;
637 let vmo = unsafe { vmo.assume_init() };
638 let vmo = unsafe { zx::NullableHandle::from_raw(vmo) };
639 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
640 Ok(zx::Vmo::from(vmo))
641}
642
643pub fn get_vmo_exec_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
645 let fd = f.as_raw_fd();
646 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
648 let status = {
649 let vmo = vmo.as_mut_ptr();
650 unsafe { fdio_sys::fdio_get_vmo_exec(fd, vmo) }
651 };
652 zx::Status::ok(status)?;
653 let vmo = unsafe { zx::NullableHandle::from_raw(vmo.assume_init()) };
654 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
655 Ok(zx::Vmo::from(vmo))
656}
657
658pub fn get_vmo_exact_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
661 let fd = f.as_raw_fd();
662 let mut vmo = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
664 let status = {
665 let vmo = vmo.as_mut_ptr();
666 unsafe { fdio_sys::fdio_get_vmo_exact(fd, vmo) }
667 };
668 let () = zx::Status::ok(status)?;
669 let vmo = unsafe { vmo.assume_init() };
670 let vmo = unsafe { zx::NullableHandle::from_raw(vmo) };
671 debug_assert!(!vmo.is_invalid(), "({:?}).is_invalid()", vmo);
672 Ok(zx::Vmo::from(vmo))
673}
674
675pub struct Namespace {
676 ns: *mut fdio_sys::fdio_ns_t,
677}
678
679impl Namespace {
680 pub fn installed() -> Result<Self, zx::Status> {
682 let mut ns = std::ptr::null_mut();
684 let status = { unsafe { fdio_sys::fdio_ns_get_installed(&mut ns) } };
685 zx::Status::ok(status)?;
686 debug_assert_ne!(ns, std::ptr::null_mut());
687 Ok(Namespace { ns })
688 }
689
690 pub fn open(
696 &self,
697 path: &str,
698 flags: fio::Flags,
699 channel: zx::Channel,
700 ) -> Result<(), zx::Status> {
701 let &Self { ns } = self;
702 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
703 let path = path.as_ptr();
704 let flags = flags.bits();
705
706 let channel = channel.into_raw();
708 let status = unsafe { fdio_sys::fdio_ns_open3(ns, path, flags, channel) };
709 zx::Status::ok(status)
710 }
711
712 pub fn bind(
718 &self,
719 path: &str,
720 channel: fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
721 ) -> Result<(), zx::Status> {
722 let &Self { ns } = self;
723 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
724 let path = path.as_ptr();
725
726 let channel = channel.into_channel().into_raw();
728 let status = unsafe { fdio_sys::fdio_ns_bind(ns, path, channel) };
729 zx::Status::ok(status)
730 }
731
732 pub fn unbind(&self, path: &str) -> Result<(), zx::Status> {
738 let &Self { ns } = self;
739 let path = CString::new(path).map_err(|_| zx::Status::INVALID_ARGS)?;
740 let path = path.as_ptr();
741 let status = unsafe { fdio_sys::fdio_ns_unbind(ns, path) };
742 zx::Status::ok(status)
743 }
744
745 pub fn export(&self) -> Result<Vec<NamespaceEntry>, zx::Status> {
748 let mut flat: *mut fdio_sys::fdio_flat_namespace_t = std::ptr::null_mut();
749
750 unsafe {
751 zx::Status::ok(fdio_sys::fdio_ns_export(self.ns, &mut flat))?;
752 let entries = Self::export_entries(flat);
753 fdio_sys::fdio_ns_free_flat_ns(flat);
754 entries
755 }
756 }
757
758 unsafe fn export_entries(
759 flat: *mut fdio_sys::fdio_flat_namespace_t,
760 ) -> Result<Vec<NamespaceEntry>, zx::Status> {
761 let fdio_sys::fdio_flat_namespace_t { count, handle, path } = unsafe { *flat };
762 let len: isize = count.try_into().map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
763 let mut entries = Vec::with_capacity(count);
764 for i in 0..len {
765 let handle = unsafe {
767 zx::NullableHandle::from_raw(mem::replace(
768 &mut *handle.offset(i),
769 zx::sys::ZX_HANDLE_INVALID,
770 ))
771 };
772 entries.push(NamespaceEntry {
773 handle,
774 path: unsafe { CStr::from_ptr(*path.offset(i)) }
775 .to_str()
776 .map_err(|_: Utf8Error| zx::Status::INVALID_ARGS)?
777 .to_owned(),
778 });
779 }
780 Ok(entries)
781 }
782
783 pub fn into_raw(self) -> *mut fdio_sys::fdio_ns_t {
784 let Self { ns } = self;
785 ns
786 }
787}
788
789#[derive(Debug)]
791pub struct NamespaceEntry {
792 pub handle: zx::NullableHandle,
793 pub path: String,
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799 use assert_matches::assert_matches;
800 use zx::{MonotonicInstant, Signals, Status, object_wait_many};
801
802 #[test]
803 fn namespace_get_installed() {
804 let namespace = Namespace::installed().expect("failed to get installed namespace");
805 assert!(!namespace.into_raw().is_null());
806 }
807
808 #[test]
809 fn namespace_bind_open_unbind() {
810 let namespace = Namespace::installed().unwrap();
811 let (ns_client, _server) = fidl::endpoints::create_endpoints();
816 let (_client, ns_server) = zx::Channel::create();
817 let path = "/test_path1";
818
819 assert_eq!(namespace.bind(path, ns_client), Ok(()));
820 assert_eq!(namespace.open(path, fio::Flags::empty(), ns_server), Ok(()));
821 assert_eq!(namespace.unbind(path), Ok(()));
822 }
823
824 #[test]
825 fn namespace_double_bind_error() {
826 let namespace = Namespace::installed().unwrap();
827 let (ns_client1, _server1) = fidl::endpoints::create_endpoints();
828 let (ns_client2, _server2) = fidl::endpoints::create_endpoints();
829 let path = "/test_path2";
830
831 assert_eq!(namespace.bind(path, ns_client1), Ok(()));
832 assert_eq!(namespace.bind(path, ns_client2), Err(zx::Status::ALREADY_EXISTS));
833 assert_eq!(namespace.unbind(path), Ok(()));
834 }
835
836 #[test]
837 fn namespace_open_error() {
838 let namespace = Namespace::installed().unwrap();
839 let path = "/test_path3";
840
841 let (_client, ns_server) = zx::Channel::create();
842 assert_eq!(
843 namespace.open(path, fio::Flags::empty(), ns_server),
844 Err(zx::Status::NOT_FOUND)
845 );
846 }
847
848 #[test]
849 fn namespace_unbind_error() {
850 let namespace = Namespace::installed().unwrap();
851 let path = "/test_path4";
852
853 assert_eq!(namespace.unbind(path), Err(zx::Status::NOT_FOUND));
854 }
855
856 #[test]
857 fn namespace_export() {
858 let namespace = Namespace::installed().unwrap();
859 let entries = namespace.export().unwrap();
860
861 assert!(!entries.is_empty());
862 }
863
864 fn cstr(orig: &str) -> CString {
865 CString::new(orig).expect("CString::new failed")
866 }
867
868 #[test]
869 fn fdio_spawn_run_target_bin_no_env() {
870 let job = zx::Job::from(zx::NullableHandle::invalid());
871 let cpath = cstr("/pkg/bin/spawn_test_target");
872 let (stdout_file, stdout_sock) = pipe_half().expect("Failed to make pipe");
873 let mut spawn_actions = [SpawnAction::clone_fd(stdout_file.as_fd(), 1)];
874
875 let cstrags: Vec<CString> = vec![cstr("test_arg")];
876 let mut cargs: Vec<&CStr> = cstrags.iter().map(|x| x.as_c_str()).collect();
877 cargs.insert(0, cpath.as_c_str());
878 let process = spawn_etc(
879 &job,
880 SpawnOptions::CLONE_ALL,
881 cpath.as_c_str(),
882 cargs.as_slice(),
883 None,
884 &mut spawn_actions,
885 )
886 .expect("Unable to spawn process");
887
888 let mut output = vec![];
889 loop {
890 let mut items = vec![
891 process.wait_item(Signals::PROCESS_TERMINATED),
892 stdout_sock.wait_item(Signals::SOCKET_READABLE | Signals::SOCKET_PEER_CLOSED),
893 ];
894
895 let signals_result =
896 object_wait_many(&mut items, MonotonicInstant::INFINITE).expect("unable to wait");
897
898 if items[1].pending().contains(Signals::SOCKET_READABLE) {
899 let bytes_len = stdout_sock.outstanding_read_bytes().expect("Socket error");
900 let mut buf: Vec<u8> = vec![0; bytes_len];
901 let read_len = stdout_sock
902 .read(&mut buf[..])
903 .or_else(|status| match status {
904 Status::SHOULD_WAIT => Ok(0),
905 _ => Err(status),
906 })
907 .expect("Unable to read buff");
908 output.extend_from_slice(&buf[0..read_len]);
909 }
910
911 if items[1].pending().contains(Signals::SOCKET_PEER_CLOSED) {
913 break;
914 }
915
916 if items[0].pending().contains(Signals::PROCESS_TERMINATED) {
917 break;
918 }
919
920 if signals_result {
921 break;
922 };
923 }
924
925 assert_eq!(String::from_utf8(output).expect("unable to decode stdout"), "hello world\n");
926 }
927
928 #[test]
932 fn fdio_open_and_open_at() {
933 use rand::distr::SampleString as _;
934
935 {
937 let (_, pkg_server) = zx::Channel::create();
938 assert_eq!(open("pkg", fio::PERM_READABLE, pkg_server), Err(zx::Status::NOT_FOUND));
939 }
940
941 let (pkg_client, pkg_server) = zx::Channel::create();
942 assert_eq!(open("/pkg", fio::PERM_READABLE, pkg_server), Ok(()));
943
944 {
946 let path = rand::distr::Alphanumeric
947 .sample_string(&mut rand::rng(), libc::PATH_MAX.try_into().unwrap());
948 let (_, server) = zx::Channel::create();
949 assert_eq!(
950 open_at(&pkg_client, &path, fio::Flags::empty(), server),
951 Err(zx::Status::INVALID_ARGS)
952 );
953 }
954
955 let (_, bin_server) = zx::Channel::create();
956 assert_eq!(open_at(&pkg_client, "bin", fio::PERM_READABLE, bin_server), Ok(()));
957 }
958
959 #[test]
963 fn fdio_open_fd_and_open_fd_at() {
964 let pkg_fd =
965 open_fd("/pkg", fio::PERM_READABLE).expect("Failed to open /pkg using fdio_open_fd");
966
967 assert_matches!(
969 open_fd_at(&pkg_fd, "blahblah", fio::PERM_READABLE),
970 Err(zx::Status::NOT_FOUND)
971 );
972
973 let _: File = open_fd_at(&pkg_fd, "bin", fio::PERM_READABLE)
974 .expect("Failed to open bin/ subdirectory using fdio_open_fd_at");
975 }
976
977 #[test]
978 fn test_create_fd_null() {
979 let fd = create_fd_null().expect("failed to create null fd");
980 use std::io::{Read, Write};
981 let mut file = std::fs::File::from(fd);
982 assert!(file.write_all(b"hello").is_ok());
983 let mut buf = [0; 10];
984 assert_eq!(file.read(&mut buf).unwrap(), 0);
985 }
986}