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::NullableHandle, zx::Status> {
146    clone_fd_inner(f.as_fd())
147}
148
149fn clone_fd_inner<'a>(fd: BorrowedFd<'a>) -> Result<zx::NullableHandle, zx::Status> {
150    // we expect fdio to initialize this to a legal value.
151    let mut handle = MaybeUninit::new(zx::NullableHandle::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::NullableHandle::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::NullableHandle, zx::Status> {
165    transfer_fd_inner(f.into())
166}
167
168fn transfer_fd_inner(fd: OwnedFd) -> Result<zx::NullableHandle, 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::NullableHandle::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::NullableHandle::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::NullableHandle) -> 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::NullableHandle, 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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle) -> 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::NullableHandle) -> 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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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::NullableHandle::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 } = unsafe { *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 = unsafe {
755                zx::NullableHandle::from_raw(mem::replace(
756                    &mut *handle.offset(i),
757                    zx::sys::ZX_HANDLE_INVALID,
758                ))
759            };
760            entries.push(NamespaceEntry {
761                handle,
762                path: unsafe { CStr::from_ptr(*path.offset(i)) }
763                    .to_str()
764                    .map_err(|_: Utf8Error| zx::Status::INVALID_ARGS)?
765                    .to_owned(),
766            });
767        }
768        Ok(entries)
769    }
770
771    pub fn into_raw(self) -> *mut fdio_sys::fdio_ns_t {
772        let Self { ns } = self;
773        ns
774    }
775}
776
777/// Entry in a flat representation of a namespace.
778#[derive(Debug)]
779pub struct NamespaceEntry {
780    pub handle: zx::NullableHandle,
781    pub path: String,
782}
783
784#[cfg(test)]
785mod tests {
786    use super::*;
787    use assert_matches::assert_matches;
788    use zx::{MonotonicInstant, Signals, Status, WaitItem, object_wait_many};
789
790    #[test]
791    fn namespace_get_installed() {
792        let namespace = Namespace::installed().expect("failed to get installed namespace");
793        assert!(!namespace.into_raw().is_null());
794    }
795
796    #[test]
797    fn namespace_bind_open_unbind() {
798        let namespace = Namespace::installed().unwrap();
799        // client => ns_server => ns_client => server
800        //        ^            ^            ^-- zx channel connection
801        //        |            |-- connected through namespace bind/connect
802        //        |-- zx channel connection
803        let (ns_client, _server) = fidl::endpoints::create_endpoints();
804        let (_client, ns_server) = zx::Channel::create();
805        let path = "/test_path1";
806
807        assert_eq!(namespace.bind(path, ns_client), Ok(()));
808        assert_eq!(namespace.open(path, fio::Flags::empty(), ns_server), Ok(()));
809        assert_eq!(namespace.unbind(path), Ok(()));
810    }
811
812    #[test]
813    fn namespace_double_bind_error() {
814        let namespace = Namespace::installed().unwrap();
815        let (ns_client1, _server1) = fidl::endpoints::create_endpoints();
816        let (ns_client2, _server2) = fidl::endpoints::create_endpoints();
817        let path = "/test_path2";
818
819        assert_eq!(namespace.bind(path, ns_client1), Ok(()));
820        assert_eq!(namespace.bind(path, ns_client2), Err(zx::Status::ALREADY_EXISTS));
821        assert_eq!(namespace.unbind(path), Ok(()));
822    }
823
824    #[test]
825    fn namespace_open_error() {
826        let namespace = Namespace::installed().unwrap();
827        let path = "/test_path3";
828
829        let (_client, ns_server) = zx::Channel::create();
830        assert_eq!(
831            namespace.open(path, fio::Flags::empty(), ns_server),
832            Err(zx::Status::NOT_FOUND)
833        );
834    }
835
836    #[test]
837    fn namespace_unbind_error() {
838        let namespace = Namespace::installed().unwrap();
839        let path = "/test_path4";
840
841        assert_eq!(namespace.unbind(path), Err(zx::Status::NOT_FOUND));
842    }
843
844    #[test]
845    fn namespace_export() {
846        let namespace = Namespace::installed().unwrap();
847        let entries = namespace.export().unwrap();
848
849        assert!(!entries.is_empty());
850    }
851
852    fn cstr(orig: &str) -> CString {
853        CString::new(orig).expect("CString::new failed")
854    }
855
856    #[test]
857    fn fdio_spawn_run_target_bin_no_env() {
858        let job = zx::Job::from(zx::NullableHandle::invalid());
859        let cpath = cstr("/pkg/bin/spawn_test_target");
860        let (stdout_file, stdout_sock) = pipe_half().expect("Failed to make pipe");
861        let mut spawn_actions = [SpawnAction::clone_fd(stdout_file.as_fd(), 1)];
862
863        let cstrags: Vec<CString> = vec![cstr("test_arg")];
864        let mut cargs: Vec<&CStr> = cstrags.iter().map(|x| x.as_c_str()).collect();
865        cargs.insert(0, cpath.as_c_str());
866        let process = spawn_etc(
867            &job,
868            SpawnOptions::CLONE_ALL,
869            cpath.as_c_str(),
870            cargs.as_slice(),
871            None,
872            &mut spawn_actions,
873        )
874        .expect("Unable to spawn process");
875
876        let mut output = vec![];
877        loop {
878            let mut items = vec![
879                WaitItem {
880                    handle: process.as_handle_ref(),
881                    waitfor: Signals::PROCESS_TERMINATED,
882                    pending: Signals::NONE,
883                },
884                WaitItem {
885                    handle: stdout_sock.as_handle_ref(),
886                    waitfor: Signals::SOCKET_READABLE | Signals::SOCKET_PEER_CLOSED,
887                    pending: Signals::NONE,
888                },
889            ];
890
891            let signals_result =
892                object_wait_many(&mut items, MonotonicInstant::INFINITE).expect("unable to wait");
893
894            if items[1].pending.contains(Signals::SOCKET_READABLE) {
895                let bytes_len = stdout_sock.outstanding_read_bytes().expect("Socket error");
896                let mut buf: Vec<u8> = vec![0; bytes_len];
897                let read_len = stdout_sock
898                    .read(&mut buf[..])
899                    .or_else(|status| match status {
900                        Status::SHOULD_WAIT => Ok(0),
901                        _ => Err(status),
902                    })
903                    .expect("Unable to read buff");
904                output.extend_from_slice(&buf[0..read_len]);
905            }
906
907            // read stdout buffer until test process dies or the socket is closed
908            if items[1].pending.contains(Signals::SOCKET_PEER_CLOSED) {
909                break;
910            }
911
912            if items[0].pending.contains(Signals::PROCESS_TERMINATED) {
913                break;
914            }
915
916            if signals_result {
917                break;
918            };
919        }
920
921        assert_eq!(String::from_utf8(output).expect("unable to decode stdout"), "hello world\n");
922    }
923
924    // Simple tests of the fdio_open and fdio_open_at wrappers. These aren't intended to
925    // exhaustively test the fdio functions - there are separate tests for that - but they do
926    // exercise one success and one failure case for each function.
927    #[test]
928    fn fdio_open_and_open_at() {
929        use rand::distr::SampleString as _;
930
931        // fdio_open requires paths to be absolute
932        {
933            let (_, pkg_server) = zx::Channel::create();
934            assert_eq!(open("pkg", fio::PERM_READABLE, pkg_server), Err(zx::Status::NOT_FOUND));
935        }
936
937        let (pkg_client, pkg_server) = zx::Channel::create();
938        assert_eq!(open("/pkg", fio::PERM_READABLE, pkg_server), Ok(()));
939
940        // fdio_open/fdio_open_at disallow paths that are too long
941        {
942            let path = rand::distr::Alphanumeric
943                .sample_string(&mut rand::rng(), libc::PATH_MAX.try_into().unwrap());
944            let (_, server) = zx::Channel::create();
945            assert_eq!(
946                open_at(&pkg_client, &path, fio::Flags::empty(), server),
947                Err(zx::Status::INVALID_ARGS)
948            );
949        }
950
951        let (_, bin_server) = zx::Channel::create();
952        assert_eq!(open_at(&pkg_client, "bin", fio::PERM_READABLE, bin_server), Ok(()));
953    }
954
955    // Simple tests of the fdio_open_fd and fdio_open_fd_at wrappers. These aren't intended to
956    // exhaustively test the fdio functions - there are separate tests for that - but they do
957    // exercise one success and one failure case for each function.
958    #[test]
959    fn fdio_open_fd_and_open_fd_at() {
960        let pkg_fd =
961            open_fd("/pkg", fio::PERM_READABLE).expect("Failed to open /pkg using fdio_open_fd");
962
963        // Trying to open a non-existent directory should fail.
964        assert_matches!(
965            open_fd_at(&pkg_fd, "blahblah", fio::PERM_READABLE),
966            Err(zx::Status::NOT_FOUND)
967        );
968
969        let _: File = open_fd_at(&pkg_fd, "bin", fio::PERM_READABLE)
970            .expect("Failed to open bin/ subdirectory using fdio_open_fd_at");
971    }
972}