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