1use crate::zxio::{
6 ZXIO_NODE_PROTOCOL_DIRECTORY, ZXIO_NODE_PROTOCOL_FILE, zxio_dirent_iterator_next,
7 zxio_dirent_iterator_t,
8};
9use bitflags::bitflags;
10use bstr::BString;
11use fidl::encoding::const_assert_eq;
12use fidl::endpoints::SynchronousProxy;
13use fidl_fuchsia_io as fio;
14use pin_weak::sync::PinWeak;
15use std::cell::OnceCell;
16use std::ffi::CStr;
17use std::marker::PhantomData;
18use std::mem::{MaybeUninit, size_of, size_of_val};
19use std::num::TryFromIntError;
20use std::os::raw::{c_char, c_int, c_uint, c_void};
21use std::pin::Pin;
22use std::sync::Arc;
23use zerocopy::{FromBytes, Immutable, IntoBytes, TryFromBytes};
24use zxio::{
25 ZXIO_SELINUX_CONTEXT_STATE_DATA, ZXIO_SHUTDOWN_OPTIONS_READ, ZXIO_SHUTDOWN_OPTIONS_WRITE,
26 msghdr, sockaddr, sockaddr_storage, socklen_t, zx_handle_t, zx_status_t, zxio_object_type_t,
27 zxio_seek_origin_t, zxio_socket_mark_t, zxio_storage_t,
28};
29
30pub mod zxio;
31
32pub use zxio::{
33 zxio_dirent_t, zxio_fsverity_descriptor, zxio_fsverity_descriptor_t,
34 zxio_node_attr_zxio_node_attr_has_t as zxio_node_attr_has_t, zxio_node_attributes_t,
35 zxio_signals_t,
36};
37
38mod inner_signals {
41 #![allow(clippy::bad_bit_mask)] use super::{bitflags, zxio_signals_t};
45
46 bitflags! {
47 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
49 pub struct ZxioSignals : zxio_signals_t {
50 const NONE = 0;
51 const READABLE = 1 << 0;
52 const WRITABLE = 1 << 1;
53 const READ_DISABLED = 1 << 2;
54 const WRITE_DISABLED = 1 << 3;
55 const READ_THRESHOLD = 1 << 4;
56 const WRITE_THRESHOLD = 1 << 5;
57 const OUT_OF_BAND = 1 << 6;
58 const ERROR = 1 << 7;
59 const PEER_CLOSED = 1 << 8;
60 }
61 }
62}
63
64pub use inner_signals::ZxioSignals;
65
66bitflags! {
67 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
69 pub struct ZxioShutdownFlags: u32 {
70 const WRITE = 1 << 0;
72
73 const READ = 1 << 1;
75 }
76}
77
78const_assert_eq!(ZxioShutdownFlags::WRITE.bits(), ZXIO_SHUTDOWN_OPTIONS_WRITE);
79const_assert_eq!(ZxioShutdownFlags::READ.bits(), ZXIO_SHUTDOWN_OPTIONS_READ);
80
81pub enum SeekOrigin {
82 Start,
83 Current,
84 End,
85}
86
87impl From<SeekOrigin> for zxio_seek_origin_t {
88 fn from(origin: SeekOrigin) -> Self {
89 match origin {
90 SeekOrigin::Start => zxio::ZXIO_SEEK_ORIGIN_START,
91 SeekOrigin::Current => zxio::ZXIO_SEEK_ORIGIN_CURRENT,
92 SeekOrigin::End => zxio::ZXIO_SEEK_ORIGIN_END,
93 }
94 }
95}
96
97#[derive(Default, Debug)]
102pub struct ZxioDirent {
103 pub protocols: Option<zxio::zxio_node_protocols_t>,
104 pub abilities: Option<zxio::zxio_abilities_t>,
105 pub id: Option<zxio::zxio_id_t>,
106 pub name: BString,
107}
108
109pub struct DirentIterator<'a> {
110 iterator: Box<zxio_dirent_iterator_t>,
111
112 _directory: PhantomData<&'a Zxio>,
115
116 finished: bool,
121}
122
123impl DirentIterator<'_> {
124 pub fn rewind(&mut self) -> Result<(), zx::Status> {
126 #[allow(
127 clippy::undocumented_unsafe_blocks,
128 reason = "Force documented unsafe blocks in Starnix"
129 )]
130 let status = unsafe { zxio::zxio_dirent_iterator_rewind(&mut *self.iterator) };
131 zx::ok(status)?;
132 self.finished = false;
133 Ok(())
134 }
135}
136
137impl Iterator for DirentIterator<'_> {
140 type Item = Result<ZxioDirent, zx::Status>;
141
142 fn next(&mut self) -> Option<Result<ZxioDirent, zx::Status>> {
144 if self.finished {
145 return None;
146 }
147 let mut entry = zxio_dirent_t::default();
148 let mut name_buffer = Vec::with_capacity(fio::MAX_NAME_LENGTH as usize);
149 entry.name = name_buffer.as_mut_ptr() as *mut c_char;
154 #[allow(
155 clippy::undocumented_unsafe_blocks,
156 reason = "Force documented unsafe blocks in Starnix"
157 )]
158 let status = unsafe { zxio_dirent_iterator_next(&mut *self.iterator.as_mut(), &mut entry) };
159 let result = match zx::ok(status) {
160 Ok(()) => {
161 let result = ZxioDirent::from(entry, name_buffer);
162 Ok(result)
163 }
164 Err(zx::Status::NOT_FOUND) => {
165 self.finished = true;
166 return None;
167 }
168 Err(e) => Err(e),
169 };
170 return Some(result);
171 }
172}
173
174impl Drop for DirentIterator<'_> {
175 fn drop(&mut self) {
176 #[allow(
177 clippy::undocumented_unsafe_blocks,
178 reason = "Force documented unsafe blocks in Starnix"
179 )]
180 unsafe {
181 zxio::zxio_dirent_iterator_destroy(&mut *self.iterator.as_mut());
182 }
183 }
184}
185
186#[allow(clippy::undocumented_unsafe_blocks, reason = "Force documented unsafe blocks in Starnix")]
187unsafe impl Send for DirentIterator<'_> {}
188#[allow(clippy::undocumented_unsafe_blocks, reason = "Force documented unsafe blocks in Starnix")]
189unsafe impl Sync for DirentIterator<'_> {}
190
191impl ZxioDirent {
192 fn from(dirent: zxio_dirent_t, name_buffer: Vec<u8>) -> ZxioDirent {
193 let protocols = if dirent.has.protocols { Some(dirent.protocols) } else { None };
194 let abilities = if dirent.has.abilities { Some(dirent.abilities) } else { None };
195 let id = if dirent.has.id { Some(dirent.id) } else { None };
196 let mut name = name_buffer;
197 #[allow(
198 clippy::undocumented_unsafe_blocks,
199 reason = "Force documented unsafe blocks in Starnix"
200 )]
201 unsafe {
202 name.set_len(dirent.name_length as usize)
203 };
204 ZxioDirent { protocols, abilities, id, name: name.into() }
205 }
206
207 pub fn is_dir(&self) -> bool {
208 self.protocols.map(|p| p & ZXIO_NODE_PROTOCOL_DIRECTORY > 0).unwrap_or(false)
209 }
210
211 pub fn is_file(&self) -> bool {
212 self.protocols.map(|p| p & ZXIO_NODE_PROTOCOL_FILE > 0).unwrap_or(false)
213 }
214}
215
216pub struct ZxioErrorCode(i16);
217impl ZxioErrorCode {
218 pub fn raw(&self) -> i16 {
219 self.0
220 }
221}
222
223#[derive(Debug, Copy, Clone, Eq, PartialEq)]
224pub enum ControlMessage {
225 IpTos(u8),
226 IpTtl(u8),
227 IpRecvOrigDstAddr([u8; size_of::<zxio::sockaddr_in>()]),
228 Ipv6Tclass(u8),
229 Ipv6HopLimit(u8),
230 Ipv6PacketInfo { iface: u32, local_addr: [u8; size_of::<zxio::in6_addr>()] },
231 Timestamp { sec: i64, usec: i64 },
232 TimestampNs { sec: i64, nsec: i64 },
233}
234
235const fn align_cmsg_size(len: usize) -> usize {
236 (len + size_of::<usize>() - 1) & !(size_of::<usize>() - 1)
237}
238
239const CMSG_HEADER_SIZE: usize = align_cmsg_size(size_of::<zxio::cmsghdr>());
240
241const MAX_CMSGS_BUFFER: usize =
244 CMSG_HEADER_SIZE * 3 + align_cmsg_size(1) * 2 + align_cmsg_size(size_of::<zxio::in6_pktinfo>());
245
246impl ControlMessage {
247 pub fn get_data_size(&self) -> usize {
248 match self {
249 ControlMessage::IpTos(_) => 1,
250 ControlMessage::IpTtl(_) => size_of::<c_int>(),
251 ControlMessage::IpRecvOrigDstAddr(addr) => size_of_val(&addr),
252 ControlMessage::Ipv6Tclass(_) => size_of::<c_int>(),
253 ControlMessage::Ipv6HopLimit(_) => size_of::<c_int>(),
254 ControlMessage::Ipv6PacketInfo { .. } => size_of::<zxio::in6_pktinfo>(),
255 ControlMessage::Timestamp { .. } => size_of::<zxio::timeval>(),
256 ControlMessage::TimestampNs { .. } => size_of::<zxio::timespec>(),
257 }
258 }
259
260 fn serialize<'a>(&'a self, out: &'a mut [u8]) -> usize {
262 let data = &mut out[CMSG_HEADER_SIZE..];
263 let (size, level, type_) = match self {
264 ControlMessage::IpTos(v) => {
265 v.write_to_prefix(data).unwrap();
266 (1, zxio::SOL_IP, zxio::IP_TOS)
267 }
268 ControlMessage::IpTtl(v) => {
269 (*v as c_int).write_to_prefix(data).unwrap();
270 (size_of::<c_int>(), zxio::SOL_IP, zxio::IP_TTL)
271 }
272 ControlMessage::IpRecvOrigDstAddr(v) => {
273 v.write_to_prefix(data).unwrap();
274 (size_of_val(&v), zxio::SOL_IP, zxio::IP_RECVORIGDSTADDR)
275 }
276 ControlMessage::Ipv6Tclass(v) => {
277 (*v as c_int).write_to_prefix(data).unwrap();
278 (size_of::<c_int>(), zxio::SOL_IPV6, zxio::IPV6_TCLASS)
279 }
280 ControlMessage::Ipv6HopLimit(v) => {
281 (*v as c_int).write_to_prefix(data).unwrap();
282 (size_of::<c_int>(), zxio::SOL_IPV6, zxio::IPV6_HOPLIMIT)
283 }
284 ControlMessage::Ipv6PacketInfo { iface, local_addr } => {
285 let pktinfo = zxio::in6_pktinfo {
286 ipi6_addr: zxio::in6_addr {
287 __in6_union: zxio::in6_addr__bindgen_ty_1 { __s6_addr: *local_addr },
288 },
289 ipi6_ifindex: *iface,
290 };
291 pktinfo.write_to_prefix(data).unwrap();
292 (size_of_val(&pktinfo), zxio::SOL_IPV6, zxio::IPV6_PKTINFO)
293 }
294 ControlMessage::Timestamp { sec, usec } => {
295 let timeval = zxio::timeval { tv_sec: *sec, tv_usec: *usec };
296 timeval.write_to_prefix(data).unwrap();
297 (size_of_val(&timeval), zxio::SOL_SOCKET, zxio::SO_TIMESTAMP)
298 }
299 ControlMessage::TimestampNs { sec, nsec } => {
300 let timespec = zxio::timespec { tv_sec: *sec, tv_nsec: *nsec };
301 timespec.write_to_prefix(data).unwrap();
302 (size_of_val(×pec), zxio::SOL_SOCKET, zxio::SO_TIMESTAMPNS)
303 }
304 };
305 let total_size = CMSG_HEADER_SIZE + size;
306 let header = zxio::cmsghdr {
307 cmsg_len: total_size as c_uint,
308 cmsg_level: level as i32,
309 cmsg_type: type_ as i32,
310 };
311 header.write_to_prefix(&mut out[..]).unwrap();
312
313 total_size
314 }
315}
316
317fn serialize_control_messages(messages: &[ControlMessage]) -> Vec<u8> {
318 let size = messages
319 .iter()
320 .fold(0, |sum, x| sum + CMSG_HEADER_SIZE + align_cmsg_size(x.get_data_size()));
321 let mut buffer = vec![0u8; size];
322 let mut pos = 0;
323 for msg in messages {
324 pos += align_cmsg_size(msg.serialize(&mut buffer[pos..]));
325 }
326 assert_eq!(pos, buffer.len());
327 buffer
328}
329
330fn parse_control_messages(data: &[u8]) -> Vec<ControlMessage> {
331 let mut result = vec![];
332 let mut pos = 0;
333 loop {
334 if pos >= data.len() {
335 return result;
336 }
337 let header_data = &data[pos..];
338 let header = match zxio::cmsghdr::read_from_prefix(header_data) {
339 Ok((h, _)) if h.cmsg_len as usize > CMSG_HEADER_SIZE => h,
340 _ => return result,
341 };
342
343 let msg_data = &data[pos + CMSG_HEADER_SIZE..pos + header.cmsg_len as usize];
344 let msg = match (header.cmsg_level as u32, header.cmsg_type as u32) {
345 (zxio::SOL_IP, zxio::IP_TOS) => {
346 ControlMessage::IpTos(u8::read_from_prefix(msg_data).unwrap().0)
347 }
348 (zxio::SOL_IP, zxio::IP_TTL) => {
349 ControlMessage::IpTtl(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
350 }
351 (zxio::SOL_IP, zxio::IP_RECVORIGDSTADDR) => ControlMessage::IpRecvOrigDstAddr(
352 <[u8; size_of::<zxio::sockaddr_in>()]>::read_from_prefix(msg_data).unwrap().0,
353 ),
354 (zxio::SOL_IPV6, zxio::IPV6_TCLASS) => {
355 ControlMessage::Ipv6Tclass(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
356 }
357 (zxio::SOL_IPV6, zxio::IPV6_HOPLIMIT) => {
358 ControlMessage::Ipv6HopLimit(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
359 }
360 (zxio::SOL_IPV6, zxio::IPV6_PKTINFO) => {
361 let pkt_info = zxio::in6_pktinfo::read_from_prefix(msg_data).unwrap().0;
362 #[allow(
363 clippy::undocumented_unsafe_blocks,
364 reason = "Force documented unsafe blocks in Starnix"
365 )]
366 ControlMessage::Ipv6PacketInfo {
367 local_addr: unsafe { pkt_info.ipi6_addr.__in6_union.__s6_addr },
368 iface: pkt_info.ipi6_ifindex,
369 }
370 }
371 (zxio::SOL_SOCKET, zxio::SO_TIMESTAMP) => {
372 let timeval = zxio::timeval::read_from_prefix(msg_data).unwrap().0;
373 ControlMessage::Timestamp { sec: timeval.tv_sec, usec: timeval.tv_usec }
374 }
375 (zxio::SOL_SOCKET, zxio::SO_TIMESTAMPNS) => {
376 let timespec = zxio::timespec::read_from_prefix(msg_data).unwrap().0;
377 ControlMessage::TimestampNs { sec: timespec.tv_sec, nsec: timespec.tv_nsec }
378 }
379 _ => panic!(
380 "ZXIO produced unexpected cmsg level={}, type={}",
381 header.cmsg_level, header.cmsg_type
382 ),
383 };
384 result.push(msg);
385
386 pos += align_cmsg_size(header.cmsg_len as usize);
387 }
388}
389
390pub struct RecvMessageInfo {
391 pub address: Vec<u8>,
392 pub bytes_read: usize,
393 pub message_length: usize,
394 pub control_messages: Vec<ControlMessage>,
395 pub flags: i32,
396}
397
398pub struct SelinuxContextAttr<'a> {
400 buf: &'a mut MaybeUninit<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>,
401 size: OnceCell<usize>,
402}
403
404impl<'a> SelinuxContextAttr<'a> {
405 pub fn new(
408 buf: &'a mut MaybeUninit<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>,
409 ) -> Self {
410 Self { buf, size: OnceCell::new() }
411 }
412
413 fn init(&mut self, size: usize) {
415 let res = self.size.set(size);
416 debug_assert!(res.is_ok());
417 }
418
419 pub fn get(&self) -> Option<&[u8]> {
421 let size = self.size.get()?;
422 Some(unsafe { self.buf.assume_init_ref()[..*size].as_ref() })
424 }
425}
426
427#[derive(Default)]
429pub struct ZxioOpenOptions<'a, 'b> {
430 attributes: Option<&'a mut zxio_node_attributes_t>,
431
432 create_attributes: Option<zxio::zxio_node_attr>,
435
436 selinux_context_read: Option<&'a mut SelinuxContextAttr<'b>>,
438}
439
440impl<'a, 'b> ZxioOpenOptions<'a, 'b> {
441 pub fn new(
445 attributes: Option<&'a mut zxio_node_attributes_t>,
446 create_attributes: Option<zxio::zxio_node_attr>,
447 ) -> Self {
448 if let Some(attrs) = &attributes {
449 validate_pointer_fields(attrs);
450 }
451 if let Some(attrs) = &create_attributes {
452 validate_pointer_fields(attrs);
453 }
454 Self { attributes, create_attributes, selinux_context_read: None }
455 }
456
457 pub fn with_selinux_context_write(mut self, context: &'a [u8]) -> Result<Self, zx::Status> {
459 if context.len() > fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize {
460 return Err(zx::Status::INVALID_ARGS);
461 }
462 const_assert_eq!(fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN, 256);
464 {
465 let create_attributes = self.create_attributes.get_or_insert_with(Default::default);
466 create_attributes.selinux_context_length = context.len() as u16;
467 create_attributes.selinux_context_state = ZXIO_SELINUX_CONTEXT_STATE_DATA;
468 create_attributes.selinux_context = context.as_ptr() as *mut u8;
471 create_attributes.has.selinux_context = true;
472 }
473 Ok(self)
474 }
475
476 pub fn with_selinux_context_read(
479 mut self,
480 context: &'a mut SelinuxContextAttr<'b>,
481 ) -> Result<Self, zx::Status> {
482 if let Some(attributes_query) = &mut self.attributes {
483 attributes_query.selinux_context = context.buf.as_mut_ptr().cast::<u8>();
484 attributes_query.has.selinux_context = true;
485 self.selinux_context_read = Some(context);
486 } else {
487 return Err(zx::Status::INVALID_ARGS);
490 }
491 Ok(self)
492 }
493
494 fn init_context_from_read(&mut self) {
496 if let (Some(attributes), Some(context)) =
497 (&self.attributes, &mut self.selinux_context_read)
498 {
499 if attributes.selinux_context_state == ZXIO_SELINUX_CONTEXT_STATE_DATA {
500 context.init(attributes.selinux_context_length as usize);
501 }
502 }
503 }
504}
505
506#[derive(Copy, Clone, Debug)]
508pub enum XattrSetMode {
509 Set = 1,
511 Create = 2,
513 Replace = 3,
515}
516
517bitflags! {
518 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
520 pub struct AllocateMode: u32 {
521 const KEEP_SIZE = 1 << 0;
522 const UNSHARE_RANGE = 1 << 1;
523 const PUNCH_HOLE = 1 << 2;
524 const COLLAPSE_RANGE = 1 << 3;
525 const ZERO_RANGE = 1 << 4;
526 const INSERT_RANGE = 1 << 5;
527 }
528}
529
530const_assert_eq!(AllocateMode::KEEP_SIZE.bits(), zxio::ZXIO_ALLOCATE_KEEP_SIZE);
531const_assert_eq!(AllocateMode::UNSHARE_RANGE.bits(), zxio::ZXIO_ALLOCATE_UNSHARE_RANGE);
532const_assert_eq!(AllocateMode::PUNCH_HOLE.bits(), zxio::ZXIO_ALLOCATE_PUNCH_HOLE);
533const_assert_eq!(AllocateMode::COLLAPSE_RANGE.bits(), zxio::ZXIO_ALLOCATE_COLLAPSE_RANGE);
534const_assert_eq!(AllocateMode::ZERO_RANGE.bits(), zxio::ZXIO_ALLOCATE_ZERO_RANGE);
535const_assert_eq!(AllocateMode::INSERT_RANGE.bits(), zxio::ZXIO_ALLOCATE_INSERT_RANGE);
536
537#[derive(Default)]
541struct ZxioStorage {
542 storage: zxio::zxio_storage_t,
543 token_resolver: Option<Arc<dyn ZxioTokenResolver>>,
544 _pin: std::marker::PhantomPinned,
545}
546
547pub struct Zxio {
552 inner: Pin<Arc<ZxioStorage>>,
553}
554
555impl Default for Zxio {
556 fn default() -> Self {
557 Self { inner: Arc::pin(ZxioStorage::default()) }
558 }
559}
560
561impl std::fmt::Debug for Zxio {
562 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
563 f.debug_struct("Zxio").finish()
564 }
565}
566
567pub struct ZxioWeak(PinWeak<ZxioStorage>);
568
569impl ZxioWeak {
570 pub fn upgrade(&self) -> Option<Zxio> {
571 Some(Zxio { inner: self.0.upgrade()? })
572 }
573}
574
575pub trait ServiceConnector {
581 fn connect(service_name: &str) -> Result<&'static zx::Channel, zx::Status>;
583}
584
585unsafe extern "C" fn service_connector<S: ServiceConnector>(
591 service_name: *const c_char,
592 provider_handle: *mut zx_handle_t,
593) -> zx_status_t {
594 let status: zx::Status = (|| {
595 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
596 let service_name = unsafe { CStr::from_ptr(service_name) }
597 .to_str()
598 .map_err(|std::str::Utf8Error { .. }| zx::Status::INVALID_ARGS)?;
599
600 S::connect(service_name).map(|channel| {
601 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
602 unsafe {
603 *provider_handle = channel.raw_handle()
604 };
605 })
606 })()
607 .into();
608 status.into_raw()
609}
610
611unsafe extern "C" fn storage_allocator(
617 _type: zxio_object_type_t,
618 out_storage: *mut *mut zxio_storage_t,
619 out_context: *mut *mut c_void,
620) -> zx_status_t {
621 let zxio_ptr_ptr = out_context as *mut *mut zxio_storage_t;
622 let status: zx::Status = (|| {
623 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
624 if let Some(zxio_ptr) = unsafe { zxio_ptr_ptr.as_mut() } {
625 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
626 if let Some(zxio) = unsafe { zxio_ptr.as_mut() } {
627 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
628 unsafe {
629 *out_storage = zxio
630 };
631 return Ok(());
632 }
633 }
634 Err(zx::Status::NO_MEMORY)
635 })()
636 .into();
637 status.into_raw()
638}
639
640fn validate_pointer_fields(attrs: &zxio_node_attributes_t) {
644 assert!(
646 attrs.fsverity_root_hash.is_null(),
647 "Passed in a pointer for the fsverity_root_hash that could not assure the lifetime."
648 );
649 assert!(
650 attrs.selinux_context.is_null(),
651 "Passed in a pointer for the selinux_context that could not assure the lifetime."
652 );
653}
654
655fn clean_pointer_fields(attrs: &mut zxio_node_attributes_t) {
658 attrs.fsverity_root_hash = std::ptr::null_mut();
659 attrs.selinux_context = std::ptr::null_mut();
660}
661
662pub const ZXIO_ROOT_HASH_LENGTH: usize = 64;
663
664#[repr(transparent)]
666#[derive(IntoBytes, TryFromBytes, Immutable)]
667pub struct ZxioSocketMark(zxio_socket_mark_t);
668
669impl ZxioSocketMark {
670 fn new(domain: u8, value: u32) -> Self {
671 ZxioSocketMark(zxio_socket_mark_t { is_present: true, domain, value, ..Default::default() })
672 }
673
674 pub fn so_mark(mark: u32) -> Self {
676 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SO_MARK as u8, mark)
677 }
678
679 pub fn uid(uid: u32) -> Self {
681 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SOCKET_UID as u8, uid)
682 }
683}
684
685#[repr(transparent)]
687pub struct ZxioWakeGroupToken(zx_handle_t);
688
689impl ZxioWakeGroupToken {
690 pub fn new(token: Option<zx::Event>) -> Self {
692 ZxioWakeGroupToken(token.map(zx::Event::into_raw).unwrap_or(zx::sys::ZX_HANDLE_INVALID))
693 }
694}
695
696#[derive(Debug, Copy, Clone, PartialEq, Eq)]
697pub enum ZxioTokenType {
698 SharingDomain,
700}
701
702impl TryFrom<zxio::zxio_token_type_t> for ZxioTokenType {
703 type Error = ();
704
705 fn try_from(value: zxio::zxio_token_type_t) -> Result<Self, Self::Error> {
706 match value {
707 zxio::ZXIO_TOKEN_TYPE_SHARING_DOMAIN => Ok(ZxioTokenType::SharingDomain),
708 _ => Err(()),
709 }
710 }
711}
712
713pub trait ZxioTokenResolver: Send + Sync {
718 fn get_token(&self, token_type: ZxioTokenType) -> Option<zx::NullableHandle>;
720}
721
722unsafe extern "C" fn resolve_token(
724 io: *mut zxio::zxio_t,
725 token_type: zxio::zxio_token_type_t,
726) -> zx_handle_t {
727 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
728 let io = unsafe { &mut *(io as *mut ZxioStorage) };
729 let Some(resolver) = io.token_resolver.as_ref() else {
730 return zx::sys::ZX_HANDLE_INVALID;
731 };
732
733 ZxioTokenType::try_from(token_type)
734 .ok()
735 .and_then(|type_| resolver.get_token(type_))
736 .map(zx::NullableHandle::into_raw)
737 .unwrap_or(zx::sys::ZX_HANDLE_INVALID)
738}
739
740pub struct ZxioSocketCreationOptions<'a> {
742 pub marks: &'a mut [ZxioSocketMark],
743 pub wake_group: ZxioWakeGroupToken,
744}
745
746impl Zxio {
747 pub fn new_socket<S: ServiceConnector>(
748 domain: c_int,
749 socket_type: c_int,
750 protocol: c_int,
751 ZxioSocketCreationOptions { marks, wake_group }: ZxioSocketCreationOptions<'_>,
752 ) -> Result<Result<Self, ZxioErrorCode>, zx::Status> {
753 let zxio = Zxio::default();
754 let mut out_context = zxio.as_storage_ptr() as *mut c_void;
755 let mut out_code = 0;
756
757 let ZxioWakeGroupToken(wake_group) = wake_group;
758 let creation_opts = zxio::zxio_socket_creation_options {
759 num_marks: marks.len(),
760 marks: marks.as_mut_ptr() as *mut _,
761 wake_group,
762 ..Default::default()
763 };
764
765 #[allow(
766 clippy::undocumented_unsafe_blocks,
767 reason = "Force documented unsafe blocks in Starnix"
768 )]
769 let status = unsafe {
770 zxio::zxio_socket_with_options(
771 Some(service_connector::<S>),
772 domain,
773 socket_type,
774 protocol,
775 creation_opts,
776 Some(storage_allocator),
777 &mut out_context as *mut *mut c_void,
778 &mut out_code,
779 )
780 };
781 zx::ok(status)?;
782 match out_code {
783 0 => Ok(Ok(zxio)),
784 _ => Ok(Err(ZxioErrorCode(out_code))),
785 }
786 }
787
788 fn as_ptr(&self) -> *mut zxio::zxio_t {
789 &self.inner.storage.io as *const zxio::zxio_t as *mut zxio::zxio_t
790 }
791
792 fn as_storage_ptr(&self) -> *mut zxio::zxio_storage_t {
793 &self.inner.storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t
794 }
795
796 pub fn create(handle: zx::NullableHandle) -> Result<Zxio, zx::Status> {
797 let zxio = Zxio::default();
798 #[allow(
799 clippy::undocumented_unsafe_blocks,
800 reason = "Force documented unsafe blocks in Starnix"
801 )]
802 let status = unsafe { zxio::zxio_create(handle.into_raw(), zxio.as_storage_ptr()) };
803 zx::ok(status)?;
804 Ok(zxio)
805 }
806
807 pub fn release(self) -> Result<zx::NullableHandle, zx::Status> {
808 let mut handle = 0;
809 #[allow(
810 clippy::undocumented_unsafe_blocks,
811 reason = "Force documented unsafe blocks in Starnix"
812 )]
813 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut handle) };
814 zx::ok(status)?;
815 #[allow(
816 clippy::undocumented_unsafe_blocks,
817 reason = "Force documented unsafe blocks in Starnix"
818 )]
819 unsafe {
820 Ok(zx::NullableHandle::from_raw(handle))
821 }
822 }
823
824 pub fn with_token_resolver(self, resolver: Arc<dyn ZxioTokenResolver>) -> Self {
827 let status = unsafe { zxio::zxio_set_token_resolver(self.as_ptr(), Some(resolve_token)) };
830 if status != zx::sys::ZX_OK && status != zx::sys::ZX_ERR_NOT_SUPPORTED {
831 panic!("Failed to set token resolver: {}", status);
832 }
833
834 let Zxio { inner } = self;
837 let mut inner = unsafe { Pin::into_inner_unchecked(inner) };
839
840 Arc::get_mut(&mut inner).expect("Expected unique ZxioStorage").token_resolver =
841 Some(resolver);
842
843 let inner = unsafe { Pin::new_unchecked(inner) };
845
846 Zxio { inner }
847 }
848
849 pub fn open(
850 &self,
851 path: &str,
852 flags: fio::Flags,
853 mut options: ZxioOpenOptions<'_, '_>,
854 ) -> Result<Self, zx::Status> {
855 let zxio = Zxio::default();
856
857 let mut zxio_open_options = zxio::zxio_open_options::default();
858 zxio_open_options.inout_attr = match &mut options.attributes {
859 Some(a) => (*a) as *mut zxio_node_attributes_t,
860 None => std::ptr::null_mut(),
861 };
862 zxio_open_options.create_attr = match &options.create_attributes {
863 Some(a) => a as *const zxio_node_attributes_t,
864 None => std::ptr::null_mut(),
865 };
866
867 #[allow(
868 clippy::undocumented_unsafe_blocks,
869 reason = "Force documented unsafe blocks in Starnix"
870 )]
871 let status = unsafe {
872 zxio::zxio_open(
873 self.as_ptr(),
874 path.as_ptr() as *const c_char,
875 path.len(),
876 flags.bits(),
877 &zxio_open_options,
878 zxio.as_storage_ptr(),
879 )
880 };
881 options.init_context_from_read();
882 if let Some(attributes) = options.attributes {
883 clean_pointer_fields(attributes);
884 }
885 zx::ok(status)?;
886 Ok(zxio)
887 }
888
889 pub fn create_with_on_representation(
890 handle: zx::NullableHandle,
891 attributes: Option<&mut zxio_node_attributes_t>,
892 ) -> Result<Zxio, zx::Status> {
893 if let Some(attr) = &attributes {
894 validate_pointer_fields(attr);
895 }
896 let zxio = Zxio::default();
897 #[allow(
898 clippy::undocumented_unsafe_blocks,
899 reason = "Force documented unsafe blocks in Starnix"
900 )]
901 let status = unsafe {
902 zxio::zxio_create_with_on_representation(
903 handle.into_raw(),
904 attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
905 zxio.as_storage_ptr(),
906 )
907 };
908 zx::ok(status)?;
909 Ok(zxio)
910 }
911
912 pub fn open_node(
914 &self,
915 path: &str,
916 flags: fio::Flags,
917 attributes: Option<&mut zxio_node_attributes_t>,
918 ) -> Result<Self, zx::Status> {
919 let zxio = Zxio::default();
920
921 #[allow(
922 clippy::undocumented_unsafe_blocks,
923 reason = "Force documented unsafe blocks in Starnix"
924 )]
925 let status = unsafe {
926 zxio::zxio_open(
927 self.as_ptr(),
928 path.as_ptr() as *const c_char,
929 path.len(),
930 (flags | fio::Flags::PROTOCOL_NODE).bits(),
933 &zxio::zxio_open_options {
934 inout_attr: attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
935 ..Default::default()
936 },
937 zxio.as_storage_ptr(),
938 )
939 };
940 zx::ok(status)?;
941 Ok(zxio)
942 }
943
944 pub fn unlink(&self, name: &str, flags: fio::UnlinkFlags) -> Result<(), zx::Status> {
945 let flags_bits = flags.bits().try_into().map_err(|_| zx::Status::INVALID_ARGS)?;
946 #[allow(
947 clippy::undocumented_unsafe_blocks,
948 reason = "Force documented unsafe blocks in Starnix"
949 )]
950 let status = unsafe {
951 zxio::zxio_unlink(self.as_ptr(), name.as_ptr() as *const c_char, name.len(), flags_bits)
952 };
953 zx::ok(status)
954 }
955
956 pub fn read(&self, data: &mut [u8]) -> Result<usize, zx::Status> {
957 let flags = zxio::zxio_flags_t::default();
958 let mut actual = 0usize;
959 #[allow(
960 clippy::undocumented_unsafe_blocks,
961 reason = "Force documented unsafe blocks in Starnix"
962 )]
963 let status = unsafe {
964 zxio::zxio_read(
965 self.as_ptr(),
966 data.as_ptr() as *mut c_void,
967 data.len(),
968 flags,
969 &mut actual,
970 )
971 };
972 zx::ok(status)?;
973 Ok(actual)
974 }
975
976 pub unsafe fn readv(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
988 let flags = zxio::zxio_flags_t::default();
989 let mut actual = 0usize;
990 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
991 let status = unsafe {
992 zxio::zxio_readv(
993 self.as_ptr(),
994 data.as_ptr() as *const zxio::zx_iovec,
995 data.len(),
996 flags,
997 &mut actual,
998 )
999 };
1000 zx::ok(status)?;
1001 Ok(actual)
1002 }
1003
1004 pub fn deep_clone(&self) -> Result<Zxio, zx::Status> {
1005 Zxio::create(self.clone_handle()?)
1006 }
1007
1008 pub fn clone_handle(&self) -> Result<zx::NullableHandle, zx::Status> {
1009 let mut handle = 0;
1010 #[allow(
1011 clippy::undocumented_unsafe_blocks,
1012 reason = "Force documented unsafe blocks in Starnix"
1013 )]
1014 let status = unsafe { zxio::zxio_clone(self.as_ptr(), &mut handle) };
1015 zx::ok(status)?;
1016 #[allow(
1017 clippy::undocumented_unsafe_blocks,
1018 reason = "Force documented unsafe blocks in Starnix"
1019 )]
1020 unsafe {
1021 Ok(zx::NullableHandle::from_raw(handle))
1022 }
1023 }
1024
1025 pub fn downgrade(&self) -> ZxioWeak {
1026 ZxioWeak(PinWeak::downgrade(self.inner.clone()))
1027 }
1028
1029 pub fn read_at(&self, offset: u64, data: &mut [u8]) -> Result<usize, zx::Status> {
1030 let flags = zxio::zxio_flags_t::default();
1031 let mut actual = 0usize;
1032 #[allow(
1033 clippy::undocumented_unsafe_blocks,
1034 reason = "Force documented unsafe blocks in Starnix"
1035 )]
1036 let status = unsafe {
1037 zxio::zxio_read_at(
1038 self.as_ptr(),
1039 offset,
1040 data.as_ptr() as *mut c_void,
1041 data.len(),
1042 flags,
1043 &mut actual,
1044 )
1045 };
1046 zx::ok(status)?;
1047 Ok(actual)
1048 }
1049
1050 pub unsafe fn readv_at(
1063 &self,
1064 offset: u64,
1065 data: &[zxio::zx_iovec],
1066 ) -> Result<usize, zx::Status> {
1067 let flags = zxio::zxio_flags_t::default();
1068 let mut actual = 0usize;
1069 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1070 let status = unsafe {
1071 zxio::zxio_readv_at(
1072 self.as_ptr(),
1073 offset,
1074 data.as_ptr() as *const zxio::zx_iovec,
1075 data.len(),
1076 flags,
1077 &mut actual,
1078 )
1079 };
1080 zx::ok(status)?;
1081 Ok(actual)
1082 }
1083
1084 pub fn write(&self, data: &[u8]) -> Result<usize, zx::Status> {
1085 let flags = zxio::zxio_flags_t::default();
1086 let mut actual = 0;
1087 #[allow(
1088 clippy::undocumented_unsafe_blocks,
1089 reason = "Force documented unsafe blocks in Starnix"
1090 )]
1091 let status = unsafe {
1092 zxio::zxio_write(
1093 self.as_ptr(),
1094 data.as_ptr() as *const c_void,
1095 data.len(),
1096 flags,
1097 &mut actual,
1098 )
1099 };
1100 zx::ok(status)?;
1101 Ok(actual)
1102 }
1103
1104 pub unsafe fn writev(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
1117 let flags = zxio::zxio_flags_t::default();
1118 let mut actual = 0;
1119 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1120 let status = unsafe {
1121 zxio::zxio_writev(
1122 self.as_ptr(),
1123 data.as_ptr() as *const zxio::zx_iovec,
1124 data.len(),
1125 flags,
1126 &mut actual,
1127 )
1128 };
1129 zx::ok(status)?;
1130 Ok(actual)
1131 }
1132
1133 pub fn write_at(&self, offset: u64, data: &[u8]) -> Result<usize, zx::Status> {
1134 let flags = zxio::zxio_flags_t::default();
1135 let mut actual = 0;
1136 #[allow(
1137 clippy::undocumented_unsafe_blocks,
1138 reason = "Force documented unsafe blocks in Starnix"
1139 )]
1140 let status = unsafe {
1141 zxio::zxio_write_at(
1142 self.as_ptr(),
1143 offset,
1144 data.as_ptr() as *const c_void,
1145 data.len(),
1146 flags,
1147 &mut actual,
1148 )
1149 };
1150 zx::ok(status)?;
1151 Ok(actual)
1152 }
1153
1154 pub unsafe fn writev_at(
1167 &self,
1168 offset: u64,
1169 data: &[zxio::zx_iovec],
1170 ) -> Result<usize, zx::Status> {
1171 let flags = zxio::zxio_flags_t::default();
1172 let mut actual = 0;
1173 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1174 let status = unsafe {
1175 zxio::zxio_writev_at(
1176 self.as_ptr(),
1177 offset,
1178 data.as_ptr() as *const zxio::zx_iovec,
1179 data.len(),
1180 flags,
1181 &mut actual,
1182 )
1183 };
1184 zx::ok(status)?;
1185 Ok(actual)
1186 }
1187
1188 pub fn truncate(&self, length: u64) -> Result<(), zx::Status> {
1189 #[allow(
1190 clippy::undocumented_unsafe_blocks,
1191 reason = "Force documented unsafe blocks in Starnix"
1192 )]
1193 let status = unsafe { zxio::zxio_truncate(self.as_ptr(), length) };
1194 zx::ok(status)?;
1195 Ok(())
1196 }
1197
1198 pub fn seek(&self, seek_origin: SeekOrigin, offset: i64) -> Result<usize, zx::Status> {
1199 let mut result = 0;
1200 #[allow(
1201 clippy::undocumented_unsafe_blocks,
1202 reason = "Force documented unsafe blocks in Starnix"
1203 )]
1204 let status =
1205 unsafe { zxio::zxio_seek(self.as_ptr(), seek_origin.into(), offset, &mut result) };
1206 zx::ok(status)?;
1207 Ok(result)
1208 }
1209
1210 pub fn vmo_get(&self, flags: zx::VmarFlags) -> Result<zx::Vmo, zx::Status> {
1211 let mut vmo = 0;
1212 #[allow(
1213 clippy::undocumented_unsafe_blocks,
1214 reason = "Force documented unsafe blocks in Starnix"
1215 )]
1216 let status = unsafe { zxio::zxio_vmo_get(self.as_ptr(), flags.bits(), &mut vmo) };
1217 zx::ok(status)?;
1218 #[allow(
1219 clippy::undocumented_unsafe_blocks,
1220 reason = "Force documented unsafe blocks in Starnix"
1221 )]
1222 let handle = unsafe { zx::NullableHandle::from_raw(vmo) };
1223 Ok(zx::Vmo::from(handle))
1224 }
1225
1226 fn node_attributes_from_query(
1227 &self,
1228 query: zxio_node_attr_has_t,
1229 fsverity_root_hash: Option<&mut [u8; ZXIO_ROOT_HASH_LENGTH]>,
1230 ) -> zxio_node_attributes_t {
1231 if let Some(fsverity_root_hash) = fsverity_root_hash {
1232 zxio_node_attributes_t {
1233 has: query,
1234 fsverity_root_hash: fsverity_root_hash as *mut u8,
1235 ..Default::default()
1236 }
1237 } else {
1238 zxio_node_attributes_t { has: query, ..Default::default() }
1239 }
1240 }
1241
1242 pub fn attr_get(
1243 &self,
1244 query: zxio_node_attr_has_t,
1245 ) -> Result<zxio_node_attributes_t, zx::Status> {
1246 let mut attributes = self.node_attributes_from_query(query, None);
1247 #[allow(
1248 clippy::undocumented_unsafe_blocks,
1249 reason = "Force documented unsafe blocks in Starnix"
1250 )]
1251 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1252 zx::ok(status)?;
1253 Ok(attributes)
1254 }
1255
1256 pub fn close_and_update_access_time(self) -> Result<(), zx::Status> {
1262 let mut out_handle = zx::sys::ZX_HANDLE_INVALID;
1263 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut out_handle) };
1265 zx::ok(status)?;
1266 let proxy = fio::NodeSynchronousProxy::from_channel(
1267 unsafe { zx::NullableHandle::from_raw(out_handle) }.into(),
1269 );
1270
1271 let _ = proxy.get_attributes(
1276 fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
1277 zx::MonotonicInstant::INFINITE_PAST,
1278 );
1279
1280 Ok(())
1283 }
1284
1285 pub fn attr_get_with_root_hash(
1287 &self,
1288 query: zxio_node_attr_has_t,
1289 fsverity_root_hash: &mut [u8; ZXIO_ROOT_HASH_LENGTH],
1290 ) -> Result<zxio_node_attributes_t, zx::Status> {
1291 let mut attributes = self.node_attributes_from_query(query, Some(fsverity_root_hash));
1292 #[allow(
1293 clippy::undocumented_unsafe_blocks,
1294 reason = "Force documented unsafe blocks in Starnix"
1295 )]
1296 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1297 clean_pointer_fields(&mut attributes);
1298 zx::ok(status)?;
1299 Ok(attributes)
1300 }
1301
1302 pub fn attr_set(&self, attributes: &zxio_node_attributes_t) -> Result<(), zx::Status> {
1303 validate_pointer_fields(attributes);
1304 #[allow(
1305 clippy::undocumented_unsafe_blocks,
1306 reason = "Force documented unsafe blocks in Starnix"
1307 )]
1308 let status = unsafe { zxio::zxio_attr_set(self.as_ptr(), attributes) };
1309 zx::ok(status)?;
1310 Ok(())
1311 }
1312
1313 pub fn enable_verity(&self, descriptor: &zxio_fsverity_descriptor_t) -> Result<(), zx::Status> {
1314 #[allow(
1315 clippy::undocumented_unsafe_blocks,
1316 reason = "Force documented unsafe blocks in Starnix"
1317 )]
1318 let status = unsafe { zxio::zxio_enable_verity(self.as_ptr(), descriptor) };
1319 zx::ok(status)?;
1320 Ok(())
1321 }
1322
1323 pub fn rename(
1324 &self,
1325 old_path: &str,
1326 new_directory: &Zxio,
1327 new_path: &str,
1328 ) -> Result<(), zx::Status> {
1329 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1330 #[allow(
1331 clippy::undocumented_unsafe_blocks,
1332 reason = "Force documented unsafe blocks in Starnix"
1333 )]
1334 let status = unsafe { zxio::zxio_token_get(new_directory.as_ptr(), &mut handle) };
1335 zx::ok(status)?;
1336 #[allow(
1337 clippy::undocumented_unsafe_blocks,
1338 reason = "Force documented unsafe blocks in Starnix"
1339 )]
1340 let status = unsafe {
1341 zxio::zxio_rename(
1342 self.as_ptr(),
1343 old_path.as_ptr() as *const c_char,
1344 old_path.len(),
1345 handle,
1346 new_path.as_ptr() as *const c_char,
1347 new_path.len(),
1348 )
1349 };
1350 zx::ok(status)?;
1351 Ok(())
1352 }
1353
1354 pub fn wait_begin(
1355 &self,
1356 zxio_signals: zxio_signals_t,
1357 ) -> (zx::Unowned<'_, zx::NullableHandle>, zx::Signals) {
1358 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1359 let mut zx_signals = zx::sys::ZX_SIGNAL_NONE;
1360 #[allow(
1361 clippy::undocumented_unsafe_blocks,
1362 reason = "Force documented unsafe blocks in Starnix"
1363 )]
1364 unsafe {
1365 zxio::zxio_wait_begin(self.as_ptr(), zxio_signals, &mut handle, &mut zx_signals)
1366 };
1367 #[allow(
1368 clippy::undocumented_unsafe_blocks,
1369 reason = "Force documented unsafe blocks in Starnix"
1370 )]
1371 let handle = unsafe { zx::Unowned::<zx::NullableHandle>::from_raw_handle(handle) };
1372 let signals = zx::Signals::from_bits_truncate(zx_signals);
1373 (handle, signals)
1374 }
1375
1376 pub fn wait_end(&self, signals: zx::Signals) -> zxio_signals_t {
1377 let mut zxio_signals = ZxioSignals::NONE.bits();
1378 #[allow(
1379 clippy::undocumented_unsafe_blocks,
1380 reason = "Force documented unsafe blocks in Starnix"
1381 )]
1382 unsafe {
1383 zxio::zxio_wait_end(self.as_ptr(), signals.bits(), &mut zxio_signals);
1384 }
1385 zxio_signals
1386 }
1387
1388 pub fn create_dirent_iterator(&self) -> Result<DirentIterator<'_>, zx::Status> {
1389 let mut zxio_iterator = Box::default();
1390 #[allow(
1391 clippy::undocumented_unsafe_blocks,
1392 reason = "Force documented unsafe blocks in Starnix"
1393 )]
1394 let status = unsafe { zxio::zxio_dirent_iterator_init(&mut *zxio_iterator, self.as_ptr()) };
1395 zx::ok(status)?;
1396 let iterator =
1397 DirentIterator { iterator: zxio_iterator, _directory: PhantomData, finished: false };
1398 Ok(iterator)
1399 }
1400
1401 pub fn connect(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1402 let mut out_code = 0;
1403 #[allow(
1404 clippy::undocumented_unsafe_blocks,
1405 reason = "Force documented unsafe blocks in Starnix"
1406 )]
1407 let status = unsafe {
1408 zxio::zxio_connect(
1409 self.as_ptr(),
1410 addr.as_ptr() as *const sockaddr,
1411 addr.len() as socklen_t,
1412 &mut out_code,
1413 )
1414 };
1415 zx::ok(status)?;
1416 match out_code {
1417 0 => Ok(Ok(())),
1418 _ => Ok(Err(ZxioErrorCode(out_code))),
1419 }
1420 }
1421
1422 pub fn bind(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1423 let mut out_code = 0;
1424 #[allow(
1425 clippy::undocumented_unsafe_blocks,
1426 reason = "Force documented unsafe blocks in Starnix"
1427 )]
1428 let status = unsafe {
1429 zxio::zxio_bind(
1430 self.as_ptr(),
1431 addr.as_ptr() as *const sockaddr,
1432 addr.len() as socklen_t,
1433 &mut out_code,
1434 )
1435 };
1436 zx::ok(status)?;
1437 match out_code {
1438 0 => Ok(Ok(())),
1439 _ => Ok(Err(ZxioErrorCode(out_code))),
1440 }
1441 }
1442
1443 pub fn listen(&self, backlog: i32) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1444 let mut out_code = 0;
1445 #[allow(
1446 clippy::undocumented_unsafe_blocks,
1447 reason = "Force documented unsafe blocks in Starnix"
1448 )]
1449 let status = unsafe { zxio::zxio_listen(self.as_ptr(), backlog as c_int, &mut out_code) };
1450 zx::ok(status)?;
1451 match out_code {
1452 0 => Ok(Ok(())),
1453 _ => Ok(Err(ZxioErrorCode(out_code))),
1454 }
1455 }
1456
1457 pub fn accept(&self) -> Result<Result<Zxio, ZxioErrorCode>, zx::Status> {
1458 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1459 let mut addr = vec![0u8; addrlen as usize];
1460 let zxio = Zxio::default();
1461 let mut out_code = 0;
1462 #[allow(
1463 clippy::undocumented_unsafe_blocks,
1464 reason = "Force documented unsafe blocks in Starnix"
1465 )]
1466 let status = unsafe {
1467 zxio::zxio_accept(
1468 self.as_ptr(),
1469 addr.as_mut_ptr() as *mut sockaddr,
1470 &mut addrlen,
1471 zxio.as_storage_ptr(),
1472 &mut out_code,
1473 )
1474 };
1475 zx::ok(status)?;
1476 match out_code {
1477 0 => Ok(Ok(zxio)),
1478 _ => Ok(Err(ZxioErrorCode(out_code))),
1479 }
1480 }
1481
1482 pub fn getsockname(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1483 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1484 let mut addr = vec![0u8; addrlen as usize];
1485 let mut out_code = 0;
1486 #[allow(
1487 clippy::undocumented_unsafe_blocks,
1488 reason = "Force documented unsafe blocks in Starnix"
1489 )]
1490 let status = unsafe {
1491 zxio::zxio_getsockname(
1492 self.as_ptr(),
1493 addr.as_mut_ptr() as *mut sockaddr,
1494 &mut addrlen,
1495 &mut out_code,
1496 )
1497 };
1498 zx::ok(status)?;
1499 match out_code {
1500 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1501 _ => Ok(Err(ZxioErrorCode(out_code))),
1502 }
1503 }
1504
1505 pub fn getpeername(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1506 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1507 let mut addr = vec![0u8; addrlen as usize];
1508 let mut out_code = 0;
1509 #[allow(
1510 clippy::undocumented_unsafe_blocks,
1511 reason = "Force documented unsafe blocks in Starnix"
1512 )]
1513 let status = unsafe {
1514 zxio::zxio_getpeername(
1515 self.as_ptr(),
1516 addr.as_mut_ptr() as *mut sockaddr,
1517 &mut addrlen,
1518 &mut out_code,
1519 )
1520 };
1521 zx::ok(status)?;
1522 match out_code {
1523 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1524 _ => Ok(Err(ZxioErrorCode(out_code))),
1525 }
1526 }
1527
1528 pub fn getsockopt_slice(
1529 &self,
1530 level: u32,
1531 optname: u32,
1532 optval: &mut [u8],
1533 ) -> Result<Result<socklen_t, ZxioErrorCode>, zx::Status> {
1534 let mut optlen = optval.len() as socklen_t;
1535 let mut out_code = 0;
1536 #[allow(
1537 clippy::undocumented_unsafe_blocks,
1538 reason = "Force documented unsafe blocks in Starnix"
1539 )]
1540 let status = unsafe {
1541 zxio::zxio_getsockopt(
1542 self.as_ptr(),
1543 level as c_int,
1544 optname as c_int,
1545 optval.as_mut_ptr() as *mut c_void,
1546 &mut optlen,
1547 &mut out_code,
1548 )
1549 };
1550 zx::ok(status)?;
1551 match out_code {
1552 0 => Ok(Ok(optlen)),
1553 _ => Ok(Err(ZxioErrorCode(out_code))),
1554 }
1555 }
1556
1557 pub fn getsockopt(
1558 &self,
1559 level: u32,
1560 optname: u32,
1561 optlen: socklen_t,
1562 ) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1563 let mut optval = vec![0u8; optlen as usize];
1564 let result = self.getsockopt_slice(level, optname, &mut optval[..])?;
1565 Ok(result.map(|optlen| optval[..optlen as usize].to_vec()))
1566 }
1567
1568 pub fn setsockopt(
1569 &self,
1570 level: i32,
1571 optname: i32,
1572 optval: &[u8],
1573 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1574 let mut out_code = 0;
1575 #[allow(
1576 clippy::undocumented_unsafe_blocks,
1577 reason = "Force documented unsafe blocks in Starnix"
1578 )]
1579 let status = unsafe {
1580 zxio::zxio_setsockopt(
1581 self.as_ptr(),
1582 level,
1583 optname,
1584 optval.as_ptr() as *const c_void,
1585 optval.len() as socklen_t,
1586 &mut out_code,
1587 )
1588 };
1589 zx::ok(status)?;
1590 match out_code {
1591 0 => Ok(Ok(())),
1592 _ => Ok(Err(ZxioErrorCode(out_code))),
1593 }
1594 }
1595
1596 pub fn shutdown(
1597 &self,
1598 flags: ZxioShutdownFlags,
1599 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1600 let mut out_code = 0;
1601 #[allow(
1602 clippy::undocumented_unsafe_blocks,
1603 reason = "Force documented unsafe blocks in Starnix"
1604 )]
1605 let status = unsafe { zxio::zxio_shutdown(self.as_ptr(), flags.bits(), &mut out_code) };
1606 zx::ok(status)?;
1607 match out_code {
1608 0 => Ok(Ok(())),
1609 _ => Ok(Err(ZxioErrorCode(out_code))),
1610 }
1611 }
1612
1613 pub fn sendmsg(
1614 &self,
1615 addr: &mut [u8],
1616 buffer: &mut [zxio::iovec],
1617 cmsg: &[ControlMessage],
1618 flags: u32,
1619 ) -> Result<Result<usize, ZxioErrorCode>, zx::Status> {
1620 let mut msg = zxio::msghdr::default();
1621 msg.msg_name = match addr.len() {
1622 0 => std::ptr::null_mut() as *mut c_void,
1623 _ => addr.as_mut_ptr() as *mut c_void,
1624 };
1625 msg.msg_namelen = addr.len() as u32;
1626
1627 msg.msg_iovlen =
1628 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1629 msg.msg_iov = buffer.as_mut_ptr();
1630
1631 let mut cmsg_buffer = serialize_control_messages(cmsg);
1632 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1633 msg.msg_controllen = cmsg_buffer.len() as u32;
1634
1635 let mut out_code = 0;
1636 let mut out_actual = 0;
1637
1638 #[allow(
1639 clippy::undocumented_unsafe_blocks,
1640 reason = "Force documented unsafe blocks in Starnix"
1641 )]
1642 let status = unsafe {
1643 zxio::zxio_sendmsg(self.as_ptr(), &msg, flags as c_int, &mut out_actual, &mut out_code)
1644 };
1645
1646 zx::ok(status)?;
1647 match out_code {
1648 0 => Ok(Ok(out_actual)),
1649 _ => Ok(Err(ZxioErrorCode(out_code))),
1650 }
1651 }
1652
1653 pub fn recvmsg(
1654 &self,
1655 buffer: &mut [zxio::iovec],
1656 flags: u32,
1657 ) -> Result<Result<RecvMessageInfo, ZxioErrorCode>, zx::Status> {
1658 let mut msg = msghdr::default();
1659 let mut addr = vec![0u8; std::mem::size_of::<sockaddr_storage>()];
1660 msg.msg_name = addr.as_mut_ptr() as *mut c_void;
1661 msg.msg_namelen = addr.len() as u32;
1662
1663 let max_buffer_capacity = buffer.iter().map(|v| v.iov_len).sum();
1664 msg.msg_iovlen =
1665 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1666 msg.msg_iov = buffer.as_mut_ptr();
1667
1668 let mut cmsg_buffer = vec![0u8; MAX_CMSGS_BUFFER];
1669 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1670 msg.msg_controllen = cmsg_buffer.len() as u32;
1671
1672 let mut out_code = 0;
1673 let mut out_actual = 0;
1674 #[allow(
1675 clippy::undocumented_unsafe_blocks,
1676 reason = "Force documented unsafe blocks in Starnix"
1677 )]
1678 let status = unsafe {
1679 zxio::zxio_recvmsg(
1680 self.as_ptr(),
1681 &mut msg,
1682 flags as c_int,
1683 &mut out_actual,
1684 &mut out_code,
1685 )
1686 };
1687 zx::ok(status)?;
1688
1689 if out_code != 0 {
1690 return Ok(Err(ZxioErrorCode(out_code)));
1691 }
1692
1693 let control_messages = parse_control_messages(&cmsg_buffer[..msg.msg_controllen as usize]);
1694 Ok(Ok(RecvMessageInfo {
1695 address: addr[..msg.msg_namelen as usize].to_vec(),
1696 bytes_read: std::cmp::min(max_buffer_capacity, out_actual),
1697 message_length: out_actual,
1698 control_messages,
1699 flags: msg.msg_flags,
1700 }))
1701 }
1702
1703 pub fn read_link(&self) -> Result<&[u8], zx::Status> {
1704 let mut target = std::ptr::null();
1705 let mut target_len = 0;
1706 #[allow(
1707 clippy::undocumented_unsafe_blocks,
1708 reason = "Force documented unsafe blocks in Starnix"
1709 )]
1710 let status = unsafe { zxio::zxio_read_link(self.as_ptr(), &mut target, &mut target_len) };
1711 zx::ok(status)?;
1712 unsafe { Ok(std::slice::from_raw_parts(target, target_len)) }
1714 }
1715
1716 pub fn create_symlink(&self, name: &str, target: &[u8]) -> Result<Zxio, zx::Status> {
1717 let name = name.as_bytes();
1718 let zxio = Zxio::default();
1719 #[allow(
1720 clippy::undocumented_unsafe_blocks,
1721 reason = "Force documented unsafe blocks in Starnix"
1722 )]
1723 let status = unsafe {
1724 zxio::zxio_create_symlink(
1725 self.as_ptr(),
1726 name.as_ptr() as *const c_char,
1727 name.len(),
1728 target.as_ptr(),
1729 target.len(),
1730 zxio.as_storage_ptr(),
1731 )
1732 };
1733 zx::ok(status)?;
1734 Ok(zxio)
1735 }
1736
1737 pub fn xattr_list(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
1738 unsafe extern "C" fn callback(context: *mut c_void, name: *const u8, name_len: usize) {
1739 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1740 let out_names = unsafe { &mut *(context as *mut Vec<Vec<u8>>) };
1741 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1742 let name_slice = unsafe { std::slice::from_raw_parts(name, name_len) };
1743 out_names.push(name_slice.to_vec());
1744 }
1745 let mut out_names = Vec::new();
1746 #[allow(
1747 clippy::undocumented_unsafe_blocks,
1748 reason = "Force documented unsafe blocks in Starnix"
1749 )]
1750 let status = unsafe {
1751 zxio::zxio_xattr_list(
1752 self.as_ptr(),
1753 Some(callback),
1754 &mut out_names as *mut _ as *mut c_void,
1755 )
1756 };
1757 zx::ok(status)?;
1758 Ok(out_names)
1759 }
1760
1761 pub fn xattr_get(&self, name: &[u8]) -> Result<Vec<u8>, zx::Status> {
1762 unsafe extern "C" fn callback(
1763 context: *mut c_void,
1764 data: zxio::zxio_xattr_data_t,
1765 ) -> zx_status_t {
1766 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1767 let out_value = unsafe { &mut *(context as *mut Vec<u8>) };
1768 if data.data.is_null() {
1769 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1770 let value_vmo = unsafe { zx::Unowned::<'_, zx::Vmo>::from_raw_handle(data.vmo) };
1771 match value_vmo.read_to_vec(0, data.len as u64) {
1772 Ok(vec) => *out_value = vec,
1773 Err(status) => return status.into_raw(),
1774 }
1775 } else {
1776 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1777 let value_slice =
1778 unsafe { std::slice::from_raw_parts(data.data as *mut u8, data.len) };
1779 out_value.extend_from_slice(value_slice);
1780 }
1781 zx::Status::OK.into_raw()
1782 }
1783 let mut out_value = Vec::new();
1784 #[allow(
1785 clippy::undocumented_unsafe_blocks,
1786 reason = "Force documented unsafe blocks in Starnix"
1787 )]
1788 let status = unsafe {
1789 zxio::zxio_xattr_get(
1790 self.as_ptr(),
1791 name.as_ptr(),
1792 name.len(),
1793 Some(callback),
1794 &mut out_value as *mut _ as *mut c_void,
1795 )
1796 };
1797 zx::ok(status)?;
1798 Ok(out_value)
1799 }
1800
1801 pub fn xattr_set(
1802 &self,
1803 name: &[u8],
1804 value: &[u8],
1805 mode: XattrSetMode,
1806 ) -> Result<(), zx::Status> {
1807 #[allow(
1808 clippy::undocumented_unsafe_blocks,
1809 reason = "Force documented unsafe blocks in Starnix"
1810 )]
1811 let status = unsafe {
1812 zxio::zxio_xattr_set(
1813 self.as_ptr(),
1814 name.as_ptr(),
1815 name.len(),
1816 value.as_ptr(),
1817 value.len(),
1818 mode as u32,
1819 )
1820 };
1821 zx::ok(status)
1822 }
1823
1824 pub fn xattr_remove(&self, name: &[u8]) -> Result<(), zx::Status> {
1825 #[allow(
1826 clippy::undocumented_unsafe_blocks,
1827 reason = "Force documented unsafe blocks in Starnix"
1828 )]
1829 zx::ok(unsafe { zxio::zxio_xattr_remove(self.as_ptr(), name.as_ptr(), name.len()) })
1830 }
1831
1832 pub fn link_into(&self, target_dir: &Zxio, name: &str) -> Result<(), zx::Status> {
1833 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1834 #[allow(
1835 clippy::undocumented_unsafe_blocks,
1836 reason = "Force documented unsafe blocks in Starnix"
1837 )]
1838 zx::ok(unsafe { zxio::zxio_token_get(target_dir.as_ptr(), &mut handle) })?;
1839 #[allow(
1840 clippy::undocumented_unsafe_blocks,
1841 reason = "Force documented unsafe blocks in Starnix"
1842 )]
1843 zx::ok(unsafe {
1844 zxio::zxio_link_into(self.as_ptr(), handle, name.as_ptr() as *const c_char, name.len())
1845 })
1846 }
1847
1848 pub fn allocate(&self, offset: u64, len: u64, mode: AllocateMode) -> Result<(), zx::Status> {
1849 #[allow(
1850 clippy::undocumented_unsafe_blocks,
1851 reason = "Force documented unsafe blocks in Starnix"
1852 )]
1853 let status = unsafe { zxio::zxio_allocate(self.as_ptr(), offset, len, mode.bits()) };
1854 zx::ok(status)
1855 }
1856
1857 pub fn sync(&self) -> Result<(), zx::Status> {
1858 #[allow(
1859 clippy::undocumented_unsafe_blocks,
1860 reason = "Force documented unsafe blocks in Starnix"
1861 )]
1862 let status = unsafe { zxio::zxio_sync(self.as_ptr()) };
1863 zx::ok(status)
1864 }
1865
1866 pub fn close(&self) -> Result<(), zx::Status> {
1867 #[allow(
1868 clippy::undocumented_unsafe_blocks,
1869 reason = "Force documented unsafe blocks in Starnix"
1870 )]
1871 let status = unsafe { zxio::zxio_close(self.as_ptr()) };
1872 zx::ok(status)
1873 }
1874}
1875
1876impl Drop for ZxioStorage {
1877 fn drop(&mut self) {
1878 let zxio_ptr: *mut zxio::zxio_t = &mut self.storage.io;
1883 #[allow(
1884 clippy::undocumented_unsafe_blocks,
1885 reason = "Force documented unsafe blocks in Starnix"
1886 )]
1887 unsafe {
1888 zxio::zxio_destroy(zxio_ptr);
1889 };
1890 }
1891}
1892
1893impl Clone for Zxio {
1894 fn clone(&self) -> Self {
1895 Self { inner: self.inner.clone() }
1896 }
1897}
1898
1899enum NodeKind {
1900 File,
1901 Directory,
1902 Symlink,
1903 Unknown,
1904}
1905
1906impl From<fio::Representation> for NodeKind {
1907 fn from(representation: fio::Representation) -> Self {
1908 match representation {
1909 fio::Representation::File(_) => NodeKind::File,
1910 fio::Representation::Directory(_) => NodeKind::Directory,
1911 fio::Representation::Symlink(_) => NodeKind::Symlink,
1912 _ => NodeKind::Unknown,
1913 }
1914 }
1915}
1916
1917struct DescribedNode {
1922 node: fio::NodeSynchronousProxy,
1923 kind: NodeKind,
1924}
1925
1926fn directory_open(
1936 directory: &fio::DirectorySynchronousProxy,
1937 path: &str,
1938 flags: fio::Flags,
1939 deadline: zx::MonotonicInstant,
1940) -> Result<DescribedNode, zx::Status> {
1941 let flags = flags | fio::Flags::FLAG_SEND_REPRESENTATION;
1942
1943 let (client_end, server_end) = zx::Channel::create();
1944 directory.open(path, flags, &Default::default(), server_end).map_err(|_| zx::Status::IO)?;
1945 let node = fio::NodeSynchronousProxy::new(client_end);
1946
1947 match node.wait_for_event(deadline).map_err(map_fidl_error)? {
1948 fio::NodeEvent::OnOpen_ { .. } => {
1949 panic!("Should never happen when sending FLAG_SEND_REPRESENTATION")
1950 }
1951 fio::NodeEvent::OnRepresentation { payload } => {
1952 Ok(DescribedNode { node, kind: payload.into() })
1953 }
1954 fio::NodeEvent::_UnknownEvent { .. } => Err(zx::Status::NOT_SUPPORTED),
1955 }
1956}
1957
1958pub fn directory_open_vmo(
1966 directory: &fio::DirectorySynchronousProxy,
1967 path: &str,
1968 vmo_flags: fio::VmoFlags,
1969 deadline: zx::MonotonicInstant,
1970) -> Result<zx::Vmo, zx::Status> {
1971 let mut flags = fio::Flags::empty();
1972 if vmo_flags.contains(fio::VmoFlags::READ) {
1973 flags |= fio::PERM_READABLE;
1974 }
1975 if vmo_flags.contains(fio::VmoFlags::WRITE) {
1976 flags |= fio::PERM_WRITABLE;
1977 }
1978 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
1979 flags |= fio::PERM_EXECUTABLE;
1980 }
1981 let description = directory_open(directory, path, flags, deadline)?;
1982 let file = match description.kind {
1983 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
1984 _ => return Err(zx::Status::IO),
1985 };
1986
1987 let vmo = file
1988 .get_backing_memory(vmo_flags, deadline)
1989 .map_err(map_fidl_error)?
1990 .map_err(zx::Status::from_raw)?;
1991 Ok(vmo)
1992}
1993
1994pub fn directory_read_file(
1999 directory: &fio::DirectorySynchronousProxy,
2000 path: &str,
2001 deadline: zx::MonotonicInstant,
2002) -> Result<Vec<u8>, zx::Status> {
2003 let description = directory_open(directory, path, fio::PERM_READABLE, deadline)?;
2004 let file = match description.kind {
2005 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
2006 _ => return Err(zx::Status::IO),
2007 };
2008
2009 let mut result = Vec::new();
2010 loop {
2011 let mut data = file
2012 .read(fio::MAX_TRANSFER_SIZE, deadline)
2013 .map_err(map_fidl_error)?
2014 .map_err(zx::Status::from_raw)?;
2015 let finished = (data.len() as u64) < fio::MAX_TRANSFER_SIZE;
2016 result.append(&mut data);
2017 if finished {
2018 return Ok(result);
2019 }
2020 }
2021}
2022
2023pub fn directory_create_tmp_file(
2025 directory: &fio::DirectorySynchronousProxy,
2026 flags: fio::Flags,
2027 deadline: zx::MonotonicInstant,
2028) -> Result<fio::FileSynchronousProxy, zx::Status> {
2029 let description = directory_open(
2030 directory,
2031 ".",
2032 flags
2033 | fio::PERM_WRITABLE
2034 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2035 | fio::Flags::PROTOCOL_FILE,
2036 deadline,
2037 )?;
2038 let file = match description.kind {
2039 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
2040 _ => return Err(zx::Status::NOT_FILE),
2041 };
2042
2043 Ok(file)
2044}
2045
2046pub fn directory_open_async(
2056 directory: &fio::DirectorySynchronousProxy,
2057 path: &str,
2058 flags: fio::Flags,
2059) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2060 if flags.intersects(fio::Flags::FLAG_SEND_REPRESENTATION) {
2061 return Err(zx::Status::INVALID_ARGS);
2062 }
2063
2064 let (proxy, server_end) = fidl::endpoints::create_sync_proxy::<fio::DirectoryMarker>();
2065 directory
2066 .open(path, flags, &Default::default(), server_end.into_channel())
2067 .map_err(|_| zx::Status::IO)?;
2068 Ok(proxy)
2069}
2070
2071pub fn directory_open_directory_async(
2082 directory: &fio::DirectorySynchronousProxy,
2083 path: &str,
2084 flags: fio::Flags,
2085) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2086 let flags = flags | fio::Flags::PROTOCOL_DIRECTORY;
2087 let proxy = directory_open_async(directory, path, flags)?;
2088 Ok(proxy)
2089}
2090
2091fn map_fidl_error(error: fidl::Error) -> zx::Status {
2092 match error {
2093 fidl::Error::ClientChannelClosed { status, .. } => status,
2094 _ => zx::Status::IO,
2095 }
2096}
2097
2098#[cfg(test)]
2099mod test {
2100 use super::*;
2101
2102 use anyhow::Error;
2103 use fidl::HandleBased;
2104 use fidl::endpoints::Proxy as _;
2105 use fuchsia_fs::directory;
2106 use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
2107
2108 fn open_pkg() -> fio::DirectorySynchronousProxy {
2109 let pkg_proxy =
2110 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2111 .expect("failed to open /pkg");
2112 fio::DirectorySynchronousProxy::new(
2113 pkg_proxy
2114 .into_channel()
2115 .expect("failed to convert proxy into channel")
2116 .into_zx_channel(),
2117 )
2118 }
2119
2120 #[fasync::run_singlethreaded(test)]
2121 async fn test_directory_open() -> Result<(), Error> {
2122 let pkg = open_pkg();
2123 let description = directory_open(
2124 &pkg,
2125 "bin/syncio_lib_test",
2126 fio::PERM_READABLE,
2127 zx::MonotonicInstant::INFINITE,
2128 )?;
2129 assert!(match description.kind {
2130 NodeKind::File => true,
2131 _ => false,
2132 });
2133 Ok(())
2134 }
2135
2136 #[fasync::run_singlethreaded(test)]
2137 async fn test_directory_open_vmo() -> Result<(), Error> {
2138 let pkg = open_pkg();
2139 let vmo = directory_open_vmo(
2140 &pkg,
2141 "bin/syncio_lib_test",
2142 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2143 zx::MonotonicInstant::INFINITE,
2144 )?;
2145 assert!(!vmo.is_invalid_handle());
2146
2147 let info = vmo.basic_info()?;
2148 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2149 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2150 Ok(())
2151 }
2152
2153 #[fasync::run_singlethreaded(test)]
2154 async fn test_directory_read_file() -> Result<(), Error> {
2155 let pkg = open_pkg();
2156 let data =
2157 directory_read_file(&pkg, "bin/syncio_lib_test", zx::MonotonicInstant::INFINITE)?;
2158
2159 assert!(!data.is_empty());
2160 Ok(())
2161 }
2162
2163 #[fasync::run_singlethreaded(test)]
2164 async fn test_directory_open_directory_async() -> Result<(), Error> {
2165 let pkg = open_pkg();
2166 let bin =
2167 directory_open_directory_async(&pkg, "bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE)?;
2168 let vmo = directory_open_vmo(
2169 &bin,
2170 "syncio_lib_test",
2171 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2172 zx::MonotonicInstant::INFINITE,
2173 )?;
2174 assert!(!vmo.is_invalid_handle());
2175
2176 let info = vmo.basic_info()?;
2177 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2178 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2179 Ok(())
2180 }
2181
2182 #[fasync::run_singlethreaded(test)]
2183 async fn test_directory_open_zxio_async() -> Result<(), Error> {
2184 let pkg_proxy =
2185 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2186 .expect("failed to open /pkg");
2187 let zx_channel = pkg_proxy
2188 .into_channel()
2189 .expect("failed to convert proxy into channel")
2190 .into_zx_channel();
2191 let storage = zxio::zxio_storage_t::default();
2192 #[allow(
2193 clippy::undocumented_unsafe_blocks,
2194 reason = "Force documented unsafe blocks in Starnix"
2195 )]
2196 let status = unsafe {
2197 zxio::zxio_create(
2198 zx_channel.into_raw(),
2199 &storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t,
2200 )
2201 };
2202 assert_eq!(status, zx::sys::ZX_OK);
2203 let io = &storage.io as *const zxio::zxio_t as *mut zxio::zxio_t;
2204 #[allow(
2205 clippy::undocumented_unsafe_blocks,
2206 reason = "Force documented unsafe blocks in Starnix"
2207 )]
2208 let close_status = unsafe { zxio::zxio_close(io) };
2209 assert_eq!(close_status, zx::sys::ZX_OK);
2210 #[allow(
2211 clippy::undocumented_unsafe_blocks,
2212 reason = "Force documented unsafe blocks in Starnix"
2213 )]
2214 unsafe {
2215 zxio::zxio_destroy(io);
2216 }
2217 Ok(())
2218 }
2219
2220 #[fuchsia::test]
2221 async fn test_directory_enumerate() -> Result<(), Error> {
2222 let pkg_dir_handle =
2223 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2224 .expect("failed to open /pkg")
2225 .into_channel()
2226 .expect("could not unwrap channel")
2227 .into_zx_channel()
2228 .into();
2229
2230 let io: Zxio = Zxio::create(pkg_dir_handle)?;
2231 let iter = io.create_dirent_iterator().expect("failed to create iterator");
2232 let expected_dir_names = vec![".", "bin", "lib", "meta"];
2233 let mut found_dir_names = iter
2234 .map(|e| {
2235 let dirent = e.expect("dirent");
2236 assert!(dirent.is_dir());
2237 std::str::from_utf8(&dirent.name).expect("name was not valid utf8").to_string()
2238 })
2239 .collect::<Vec<_>>();
2240 found_dir_names.sort();
2241 assert_eq!(expected_dir_names, found_dir_names);
2242
2243 let bin_io = io
2245 .open("bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE, Default::default())
2246 .expect("open");
2247 for entry in bin_io.create_dirent_iterator().expect("failed to create iterator") {
2248 let dirent = entry.expect("dirent");
2249 if dirent.name == "." {
2250 assert!(dirent.is_dir());
2251 } else {
2252 assert!(dirent.is_file());
2253 }
2254 }
2255
2256 Ok(())
2257 }
2258
2259 #[fuchsia::test]
2260 fn test_storage_allocator() {
2261 let mut out_storage = zxio_storage_t::default();
2262 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2263
2264 let mut out_context = Zxio::default();
2265 let mut out_context_ptr = &mut out_context as *mut Zxio;
2266
2267 #[allow(
2268 clippy::undocumented_unsafe_blocks,
2269 reason = "Force documented unsafe blocks in Starnix"
2270 )]
2271 let out = unsafe {
2272 storage_allocator(
2273 0 as zxio_object_type_t,
2274 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2275 &mut out_context_ptr as *mut *mut Zxio as *mut *mut c_void,
2276 )
2277 };
2278 assert_eq!(out, zx::sys::ZX_OK);
2279 }
2280
2281 #[fuchsia::test]
2282 fn test_storage_allocator_bad_context() {
2283 let mut out_storage = zxio_storage_t::default();
2284 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2285
2286 let out_context = std::ptr::null_mut();
2287
2288 #[allow(
2289 clippy::undocumented_unsafe_blocks,
2290 reason = "Force documented unsafe blocks in Starnix"
2291 )]
2292 let out = unsafe {
2293 storage_allocator(
2294 0 as zxio_object_type_t,
2295 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2296 out_context,
2297 )
2298 };
2299 assert_eq!(out, zx::sys::ZX_ERR_NO_MEMORY);
2300 }
2301}