Skip to main content

fdio/
lib.rs

1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Bindings for the Zircon fdio library
6
7mod 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
26/// Connects a channel to a named service.
27pub 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    // The channel is always consumed.
33    let channel = channel.into_raw();
34    let status = unsafe { fdio_sys::fdio_service_connect(service_path, channel) };
35    zx::Status::ok(status)
36}
37
38/// Connects a channel to a named service relative to a directory `dir`.
39/// `dir` must be a directory protocol channel.
40pub 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    // The channel is always consumed.
51    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
56/// Opens the remote object at the given `path` with the given `flags` asynchronously.
57/// ('asynchronous' here is referring to fuchsia.io.Directory.Open not having a return value).
58///
59/// Wraps fdio_open3.
60pub 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    // The channel is always consumed.
66    let channel = channel.into_raw();
67    let status = unsafe { fdio_sys::fdio_open3(path, flags, channel) };
68    zx::Status::ok(status)
69}
70
71/// Opens the remote object at the given `path` relative to the given `dir` with the given `flags`
72/// asynchronously. ('asynchronous' here is referring to fuchsia.io.Directory.Open not having a
73/// return value).
74///
75/// `dir` must be a directory protocol channel.
76///
77/// Wraps fdio_open3_at.
78pub 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    // The channel is always consumed.
90    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
95/// Opens the remote object at the given `path` with the given `flags` synchronously, and on
96/// success, binds that channel to a file descriptor and returns it.
97///
98/// Wraps fdio_open3_fd.
99pub 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    // file descriptors are always positive; we expect fdio to initialize this to a legal value.
105    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
117/// Opens the remote object at the given `path` relative to the given `dir` with the given `flags`
118/// synchronously, and on success, binds that channel to a file descriptor and returns it.
119///
120/// `dir` must be backed by a directory protocol channel (even though it is
121/// wrapped in a std::fs::File).
122///
123/// Wraps fdio_open3_fd_at.
124pub 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    // file descriptors are always positive; we expect fdio to initialize this to a legal value.
131    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
143/// Clones an object's underlying handle.
144pub 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    // we expect fdio to initialize this to a legal value.
150    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
162/// Removes an object from the file descriptor table and returns its underlying handle.
163pub 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    // we expect fdio to initialize this to a legal value.
170    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
182/// Create an object from a handle.
183///
184/// Afterward, the handle is owned by fdio, and will close with `OwnedFd`.
185/// See `transfer_fd` for a way to get it back.
186pub fn create_fd(handle: zx::NullableHandle) -> Result<OwnedFd, zx::Status> {
187    let handle = handle.into_raw();
188    // file descriptors are always positive; we expect fdio to initialize this to a legal value.
189    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    // Safety: The handle is now owned by fdio, so it does not require any other cleanup.
198    let f = unsafe { OwnedFd::from_raw_fd(fd) };
199    Ok(f)
200}
201
202/// Bind a handle to a specific file descriptor.
203///
204/// Afterward, the handle is owned by fdio, and will close when the file descriptor is closed.
205/// See `transfer_fd` for a way to get it back.
206pub fn bind_to_fd(handle: zx::NullableHandle, fd: RawFd) -> Result<(), zx::Status> {
207    if fd < 0 {
208        // fdio_bind_to_fd supports finding the next available fd when provided with a negative
209        // number, but due to lack of use-cases for this in Rust this is currently unsupported by
210        // this function.
211        return Err(zx::Status::INVALID_ARGS);
212    }
213
214    // The handle is always consumed.
215    let handle = handle.into_raw();
216    // we expect fdio to initialize this to a legal value.
217    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    // The fdio object is always consumed.
224    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    // We requested a specific fd, we expect to have gotten it, or failed.
229    assert_eq!(bound_fd, fd);
230    Ok(())
231}
232
233/// Clones an object's underlying handle and checks that it is a channel.
234pub 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
248/// Creates a named pipe and returns one end as a zx::Socket.
249pub fn pipe_half() -> Result<(File, zx::Socket), zx::Status> {
250    // file descriptors are always positive; we expect fdio to initialize this to a legal value.
251    let mut fd = MaybeUninit::new(-1);
252    // we expect fdio to initialize this to a legal value.
253    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
269/// Creates a transferrable object and returns one end as a zx::Channel.
270pub fn create_transferrable() -> Result<(File, zx::Channel), zx::Status> {
271    // We expect fdio to initialize this to a legal value.
272    let mut fd = MaybeUninit::new(-1);
273    // We expect fdio to initialize this to a legal value.
274    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    /// Options to allow some or all of the environment of the running process
290    /// to be shared with the process being spawned.
291    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
292    pub struct SpawnOptions: u32 {
293        /// Provide the spawned process with the job in which the process was created.
294        ///
295        /// The job will be available to the new process as the PA_JOB_DEFAULT argument
296        /// (exposed in Rust as `fuchsia_runtim::job_default()`).
297        const CLONE_JOB = fdio_sys::FDIO_SPAWN_CLONE_JOB;
298
299        /// Provide the spawned process with the shared library loader via the
300        /// PA_LDSVC_LOADER argument.
301        const DEFAULT_LOADER = fdio_sys::FDIO_SPAWN_DEFAULT_LDSVC;
302
303        /// Clones the filesystem namespace into the spawned process.
304        const CLONE_NAMESPACE = fdio_sys::FDIO_SPAWN_CLONE_NAMESPACE;
305
306        /// Clones file descriptors 0, 1, and 2 into the spawned process.
307        ///
308        /// Skips any of these file descriptors that are closed without
309        /// generating an error.
310        const CLONE_STDIO = fdio_sys::FDIO_SPAWN_CLONE_STDIO;
311
312        /// Clones the environment into the spawned process.
313        const CLONE_ENVIRONMENT = fdio_sys::FDIO_SPAWN_CLONE_ENVIRON;
314
315        /// Clones the namespace, stdio, and environment into the spawned process.
316        const CLONE_ALL = fdio_sys::FDIO_SPAWN_CLONE_ALL;
317    }
318}
319
320// TODO: someday we'll have custom DSTs which will make this unnecessary.
321fn 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
325/// Spawn a process in the given `job`.
326pub 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    // we expect fdio to initialize this to a legal value.
337    let mut process = MaybeUninit::new(zx::NullableHandle::invalid().raw_handle());
338
339    // Safety: spawn consumes no handles and frees no pointers, and only
340    // produces a valid process upon success.
341    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/// An action to take in `spawn_etc`.
354#[repr(transparent)]
355pub struct SpawnAction<'a>(fdio_sys::fdio_spawn_action_t, PhantomData<&'a ()>);
356
357// TODO(https://github.com/rust-lang/rust-bindgen/issues/2000): bindgen
358// generates really bad names for these.
359mod 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    /// Clone a file descriptor into the new process.
375    ///
376    /// `local_fd`: File descriptor within the current process.
377    /// `target_fd`: File descriptor within the new process that will receive the clone.
378    pub fn clone_fd(local_fd: BorrowedFd<'a>, target_fd: i32) -> Self {
379        let local_fd = local_fd.as_fd();
380        // Safety: `local_fd` is a valid file descriptor so long as we're inside the
381        // 'a lifetime.
382        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    /// Transfer a file descriptor into the new process.
394    ///
395    /// `local_fd`: File descriptor within the current process.
396    /// `target_fd`: File descriptor within the new process that will receive the transfer.
397    pub fn transfer_fd(local_fd: OwnedFd, target_fd: i32) -> Self {
398        // Safety: ownership of `local_fd` is consumed, so `Self` can live arbitrarily long.
399        // When the action is executed, the fd will be transferred.
400        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    /// Add the given entry to the namespace of the spawned process.
412    ///
413    /// If `SpawnOptions::CLONE_NAMESPACE` is set, the namespace entry is added
414    /// to the cloned namespace from the calling process.
415    pub fn add_namespace_entry(prefix: &'a CStr, handle: zx::NullableHandle) -> Self {
416        // Safety: ownership of the `handle` is consumed.
417        // The prefix string must stay valid through the 'a lifetime.
418        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    /// Add the given handle to the process arguments of the spawned process.
433    pub fn add_handle(kind: fuchsia_runtime::HandleInfo, handle: zx::NullableHandle) -> Self {
434        // Safety: ownership of the `handle` is consumed.
435        // The prefix string must stay valid through the 'a lifetime.
436        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    /// Sets the name of the spawned process to the given name.
448    pub fn set_name(name: &'a CStr) -> Self {
449        // Safety: the `name` pointer must be valid at least as long as `Self`.
450        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    /// Nullifies the action to prevent the inner contents from being dropped.
467    fn nullify(&mut self) {
468        // Assert that our null value doesn't conflict with any "real" actions.
469        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,                 // job
491        u32,                                  // flags
492        *const *const raw::c_char,            // argv
493        *const *const raw::c_char,            // environ
494        usize,                                // action_count
495        *const fdio_sys::fdio_spawn_action_t, // actions
496        *mut zx::sys::zx_handle_t,            // process_out,
497        *mut raw::c_char,                     // err_msg_out
498    ) -> 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    // we expect fdio to initialize this to a legal value.
510    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    // Statically verify this hasn't been moved out of during the call above;
528    // raw pointers escape the borrow checker.
529    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    // Clear out the actions so we can't unsafely re-use them in a future call.
539    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
547/// Spawn a process in the given `job` using a series of `SpawnAction`s.
548/// All `SpawnAction`s are nullified after their use in this function.
549pub 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
580/// Spawn a process in the given job using an executable VMO.
581pub 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
612/// Gets a read-only VMO containing the whole contents of the file. This function
613/// creates a clone of the underlying VMO when possible, falling back to eagerly
614/// reading the contents into a freshly-created VMO.
615pub fn get_vmo_copy_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
616    let fd = f.as_raw_fd();
617    // we expect fdio to initialize this to a legal value.
618    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
630/// Gets a read-exec VMO containing the whole contents of the file.
631pub fn get_vmo_exec_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
632    let fd = f.as_raw_fd();
633    // we expect fdio to initialize this to a legal value.
634    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
645/// Get a read-only handle to the exact VMO used by the file system server to represent the file.
646/// This VMO will track size and content changes to the file.
647pub fn get_vmo_exact_from_file(f: &File) -> Result<zx::Vmo, zx::Status> {
648    let fd = f.as_raw_fd();
649    // we expect fdio to initialize this to a legal value.
650    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    /// Get the currently installed namespace.
668    pub fn installed() -> Result<Self, zx::Status> {
669        // we expect fdio to initialize this to a legal value.
670        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    /// Open an object at |path| relative to the root of this namespace with |flags|.
678    ///
679    /// |path| must be absolute.
680    ///
681    /// This corresponds with fdio_ns_open3 in C.
682    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        // The channel is always consumed.
694        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    /// Create a new directory within the namespace, bound to the provided
700    /// directory-protocol-compatible channel. The path must be an absolute path, like "/x/y/z",
701    /// containing no "." nor ".." entries. It is relative to the root of the namespace.
702    ///
703    /// This corresponds with fdio_ns_bind in C.
704    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        // The channel is always consumed.
714        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    /// Unbind the channel at path, closing the associated handle when all references to the node go
720    /// out of scope. The path must be an absolute path, like "/x/y/z", containing no "." nor ".."
721    /// entries. It is relative to the root of the namespace.
722    ///
723    /// This corresponds with fdio_ns_unbind in C.
724    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    // Export this namespace, by returning a flat representation of it. The
733    // handles returned are clones of the handles within the namespace.
734    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            // Explicitly take ownership of the handle, and invalidate the source.
753            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/// Entry in a flat representation of a namespace.
777#[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        // client => ns_server => ns_client => server
799        //        ^            ^            ^-- zx channel connection
800        //        |            |-- connected through namespace bind/connect
801        //        |-- zx channel connection
802        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            // read stdout buffer until test process dies or the socket is closed
899            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    // Simple tests of the fdio_open and fdio_open_at wrappers. These aren't intended to
916    // exhaustively test the fdio functions - there are separate tests for that - but they do
917    // exercise one success and one failure case for each function.
918    #[test]
919    fn fdio_open_and_open_at() {
920        use rand::distr::SampleString as _;
921
922        // fdio_open requires paths to be absolute
923        {
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        // fdio_open/fdio_open_at disallow paths that are too long
932        {
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    // Simple tests of the fdio_open_fd and fdio_open_fd_at wrappers. These aren't intended to
947    // exhaustively test the fdio functions - there are separate tests for that - but they do
948    // exercise one success and one failure case for each function.
949    #[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        // Trying to open a non-existent directory should fail.
955        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}