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 IpPacketInfo {
231 iface: c_int,
232 local_addr: [u8; size_of::<zxio::in_addr>()],
233 header_destination_addr: [u8; size_of::<zxio::in_addr>()],
234 },
235 Ipv6PacketInfo {
236 iface: u32,
237 local_addr: [u8; size_of::<zxio::in6_addr>()],
238 },
239 Timestamp {
240 sec: i64,
241 usec: i64,
242 },
243 TimestampNs {
244 sec: i64,
245 nsec: i64,
246 },
247}
248
249const fn align_cmsg_size(len: usize) -> usize {
250 (len + size_of::<usize>() - 1) & !(size_of::<usize>() - 1)
251}
252
253const CMSG_HEADER_SIZE: usize = align_cmsg_size(size_of::<zxio::cmsghdr>());
254
255const MAX_CMSGS_BUFFER: usize =
258 CMSG_HEADER_SIZE * 3 + align_cmsg_size(1) * 2 + align_cmsg_size(size_of::<zxio::in6_pktinfo>());
259
260impl ControlMessage {
261 pub fn get_data_size(&self) -> usize {
262 match self {
263 ControlMessage::IpTos(_) => 1,
264 ControlMessage::IpTtl(_) => size_of::<c_int>(),
265 ControlMessage::IpRecvOrigDstAddr(addr) => size_of_val(&addr),
266 ControlMessage::Ipv6Tclass(_) => size_of::<c_int>(),
267 ControlMessage::Ipv6HopLimit(_) => size_of::<c_int>(),
268 ControlMessage::IpPacketInfo { .. } => size_of::<zxio::in_pktinfo>(),
269 ControlMessage::Ipv6PacketInfo { .. } => size_of::<zxio::in6_pktinfo>(),
270 ControlMessage::Timestamp { .. } => size_of::<zxio::timeval>(),
271 ControlMessage::TimestampNs { .. } => size_of::<zxio::timespec>(),
272 }
273 }
274
275 fn serialize<'a>(&'a self, out: &'a mut [u8]) -> usize {
277 let data = &mut out[CMSG_HEADER_SIZE..];
278 let (size, level, type_) = match self {
279 ControlMessage::IpTos(v) => {
280 v.write_to_prefix(data).unwrap();
281 (1, zxio::SOL_IP, zxio::IP_TOS)
282 }
283 ControlMessage::IpTtl(v) => {
284 (*v as c_int).write_to_prefix(data).unwrap();
285 (size_of::<c_int>(), zxio::SOL_IP, zxio::IP_TTL)
286 }
287 ControlMessage::IpRecvOrigDstAddr(v) => {
288 v.write_to_prefix(data).unwrap();
289 (size_of_val(&v), zxio::SOL_IP, zxio::IP_RECVORIGDSTADDR)
290 }
291 ControlMessage::Ipv6Tclass(v) => {
292 (*v as c_int).write_to_prefix(data).unwrap();
293 (size_of::<c_int>(), zxio::SOL_IPV6, zxio::IPV6_TCLASS)
294 }
295 ControlMessage::Ipv6HopLimit(v) => {
296 (*v as c_int).write_to_prefix(data).unwrap();
297 (size_of::<c_int>(), zxio::SOL_IPV6, zxio::IPV6_HOPLIMIT)
298 }
299 ControlMessage::IpPacketInfo { iface, local_addr, header_destination_addr } => {
300 let pktinfo = zxio::in_pktinfo {
301 ipi_ifindex: *iface,
302 ipi_spec_dst: zxio::in_addr { s_addr: u32::from_ne_bytes(*local_addr) },
303 ipi_addr: zxio::in_addr {
304 s_addr: u32::from_ne_bytes(*header_destination_addr),
305 },
306 };
307 pktinfo.write_to_prefix(data).unwrap();
308 (size_of_val(&pktinfo), zxio::SOL_IP, zxio::IP_PKTINFO)
309 }
310 ControlMessage::Ipv6PacketInfo { iface, local_addr } => {
311 let pktinfo = zxio::in6_pktinfo {
312 ipi6_addr: zxio::in6_addr {
313 __in6_union: zxio::in6_addr__bindgen_ty_1 { __s6_addr: *local_addr },
314 },
315 ipi6_ifindex: *iface,
316 };
317 pktinfo.write_to_prefix(data).unwrap();
318 (size_of_val(&pktinfo), zxio::SOL_IPV6, zxio::IPV6_PKTINFO)
319 }
320 ControlMessage::Timestamp { sec, usec } => {
321 let timeval = zxio::timeval { tv_sec: *sec, tv_usec: *usec };
322 timeval.write_to_prefix(data).unwrap();
323 (size_of_val(&timeval), zxio::SOL_SOCKET, zxio::SO_TIMESTAMP)
324 }
325 ControlMessage::TimestampNs { sec, nsec } => {
326 let timespec = zxio::timespec { tv_sec: *sec, tv_nsec: *nsec };
327 timespec.write_to_prefix(data).unwrap();
328 (size_of_val(×pec), zxio::SOL_SOCKET, zxio::SO_TIMESTAMPNS)
329 }
330 };
331 let total_size = CMSG_HEADER_SIZE + size;
332 let header = zxio::cmsghdr {
333 cmsg_len: total_size as c_uint,
334 cmsg_level: level as i32,
335 cmsg_type: type_ as i32,
336 };
337 header.write_to_prefix(&mut out[..]).unwrap();
338
339 total_size
340 }
341}
342
343fn serialize_control_messages(messages: &[ControlMessage]) -> Vec<u8> {
344 let size = messages
345 .iter()
346 .fold(0, |sum, x| sum + CMSG_HEADER_SIZE + align_cmsg_size(x.get_data_size()));
347 let mut buffer = vec![0u8; size];
348 let mut pos = 0;
349 for msg in messages {
350 pos += align_cmsg_size(msg.serialize(&mut buffer[pos..]));
351 }
352 assert_eq!(pos, buffer.len());
353 buffer
354}
355
356fn parse_control_messages(data: &[u8]) -> Vec<ControlMessage> {
357 let mut result = vec![];
358 let mut pos = 0;
359 loop {
360 if pos >= data.len() {
361 return result;
362 }
363 let header_data = &data[pos..];
364 let header = match zxio::cmsghdr::read_from_prefix(header_data) {
365 Ok((h, _)) if h.cmsg_len as usize > CMSG_HEADER_SIZE => h,
366 _ => return result,
367 };
368
369 let msg_data = &data[pos + CMSG_HEADER_SIZE..pos + header.cmsg_len as usize];
370 let msg = match (header.cmsg_level as u32, header.cmsg_type as u32) {
371 (zxio::SOL_IP, zxio::IP_TOS) => {
372 ControlMessage::IpTos(u8::read_from_prefix(msg_data).unwrap().0)
373 }
374 (zxio::SOL_IP, zxio::IP_TTL) => {
375 ControlMessage::IpTtl(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
376 }
377 (zxio::SOL_IP, zxio::IP_RECVORIGDSTADDR) => ControlMessage::IpRecvOrigDstAddr(
378 <[u8; size_of::<zxio::sockaddr_in>()]>::read_from_prefix(msg_data).unwrap().0,
379 ),
380 (zxio::SOL_IP, zxio::IP_PKTINFO) => {
381 let pkt_info = zxio::in_pktinfo::read_from_prefix(msg_data).unwrap().0;
382 ControlMessage::IpPacketInfo {
383 iface: pkt_info.ipi_ifindex,
384 local_addr: pkt_info.ipi_spec_dst.s_addr.to_ne_bytes(),
385 header_destination_addr: pkt_info.ipi_addr.s_addr.to_ne_bytes(),
386 }
387 }
388 (zxio::SOL_IPV6, zxio::IPV6_TCLASS) => {
389 ControlMessage::Ipv6Tclass(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
390 }
391 (zxio::SOL_IPV6, zxio::IPV6_HOPLIMIT) => {
392 ControlMessage::Ipv6HopLimit(c_int::read_from_prefix(msg_data).unwrap().0 as u8)
393 }
394 (zxio::SOL_IPV6, zxio::IPV6_PKTINFO) => {
395 let pkt_info = zxio::in6_pktinfo::read_from_prefix(msg_data).unwrap().0;
396 #[allow(
397 clippy::undocumented_unsafe_blocks,
398 reason = "Force documented unsafe blocks in Starnix"
399 )]
400 ControlMessage::Ipv6PacketInfo {
401 local_addr: unsafe { pkt_info.ipi6_addr.__in6_union.__s6_addr },
402 iface: pkt_info.ipi6_ifindex,
403 }
404 }
405 (zxio::SOL_SOCKET, zxio::SO_TIMESTAMP) => {
406 let timeval = zxio::timeval::read_from_prefix(msg_data).unwrap().0;
407 ControlMessage::Timestamp { sec: timeval.tv_sec, usec: timeval.tv_usec }
408 }
409 (zxio::SOL_SOCKET, zxio::SO_TIMESTAMPNS) => {
410 let timespec = zxio::timespec::read_from_prefix(msg_data).unwrap().0;
411 ControlMessage::TimestampNs { sec: timespec.tv_sec, nsec: timespec.tv_nsec }
412 }
413 _ => panic!(
414 "ZXIO produced unexpected cmsg level={}, type={}",
415 header.cmsg_level, header.cmsg_type
416 ),
417 };
418 result.push(msg);
419
420 pos += align_cmsg_size(header.cmsg_len as usize);
421 }
422}
423
424pub struct RecvMessageInfo {
425 pub address: Vec<u8>,
426 pub bytes_read: usize,
427 pub message_length: usize,
428 pub control_messages: Vec<ControlMessage>,
429 pub flags: i32,
430}
431
432pub struct SelinuxContextAttr<'a> {
434 buf: &'a mut MaybeUninit<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>,
435 size: OnceCell<usize>,
436}
437
438impl<'a> SelinuxContextAttr<'a> {
439 pub fn new(
442 buf: &'a mut MaybeUninit<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>,
443 ) -> Self {
444 Self { buf, size: OnceCell::new() }
445 }
446
447 fn init(&mut self, size: usize) {
449 let res = self.size.set(size);
450 debug_assert!(res.is_ok());
451 }
452
453 pub fn get(&self) -> Option<&[u8]> {
455 let size = self.size.get()?;
456 Some(unsafe { self.buf.assume_init_ref()[..*size].as_ref() })
458 }
459}
460
461#[derive(Default)]
463pub struct ZxioOpenOptions<'a, 'b> {
464 attributes: Option<&'a mut zxio_node_attributes_t>,
465
466 create_attributes: Option<zxio::zxio_node_attr>,
469
470 selinux_context_read: Option<&'a mut SelinuxContextAttr<'b>>,
472}
473
474impl<'a, 'b> ZxioOpenOptions<'a, 'b> {
475 pub fn new(
479 attributes: Option<&'a mut zxio_node_attributes_t>,
480 create_attributes: Option<zxio::zxio_node_attr>,
481 ) -> Self {
482 if let Some(attrs) = &attributes {
483 validate_pointer_fields(attrs);
484 }
485 if let Some(attrs) = &create_attributes {
486 validate_pointer_fields(attrs);
487 }
488 Self { attributes, create_attributes, selinux_context_read: None }
489 }
490
491 pub fn with_selinux_context_write(mut self, context: &'a [u8]) -> Result<Self, zx::Status> {
493 if context.len() > fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize {
494 return Err(zx::Status::INVALID_ARGS);
495 }
496 const_assert_eq!(fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN, 256);
498 {
499 let create_attributes = self.create_attributes.get_or_insert_with(Default::default);
500 create_attributes.selinux_context_length = context.len() as u16;
501 create_attributes.selinux_context_state = ZXIO_SELINUX_CONTEXT_STATE_DATA;
502 create_attributes.selinux_context = context.as_ptr() as *mut u8;
505 create_attributes.has.selinux_context = true;
506 }
507 Ok(self)
508 }
509
510 pub fn with_selinux_context_read(
513 mut self,
514 context: &'a mut SelinuxContextAttr<'b>,
515 ) -> Result<Self, zx::Status> {
516 if let Some(attributes_query) = &mut self.attributes {
517 attributes_query.selinux_context = context.buf.as_mut_ptr().cast::<u8>();
518 attributes_query.has.selinux_context = true;
519 self.selinux_context_read = Some(context);
520 } else {
521 return Err(zx::Status::INVALID_ARGS);
524 }
525 Ok(self)
526 }
527
528 fn init_context_from_read(&mut self) {
530 if let (Some(attributes), Some(context)) =
531 (&self.attributes, &mut self.selinux_context_read)
532 {
533 if attributes.selinux_context_state == ZXIO_SELINUX_CONTEXT_STATE_DATA {
534 context.init(attributes.selinux_context_length as usize);
535 }
536 }
537 }
538}
539
540#[derive(Copy, Clone, Debug)]
542pub enum XattrSetMode {
543 Set = 1,
545 Create = 2,
547 Replace = 3,
549}
550
551bitflags! {
552 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
554 pub struct AllocateMode: u32 {
555 const KEEP_SIZE = 1 << 0;
556 const UNSHARE_RANGE = 1 << 1;
557 const PUNCH_HOLE = 1 << 2;
558 const COLLAPSE_RANGE = 1 << 3;
559 const ZERO_RANGE = 1 << 4;
560 const INSERT_RANGE = 1 << 5;
561 }
562}
563
564const_assert_eq!(AllocateMode::KEEP_SIZE.bits(), zxio::ZXIO_ALLOCATE_KEEP_SIZE);
565const_assert_eq!(AllocateMode::UNSHARE_RANGE.bits(), zxio::ZXIO_ALLOCATE_UNSHARE_RANGE);
566const_assert_eq!(AllocateMode::PUNCH_HOLE.bits(), zxio::ZXIO_ALLOCATE_PUNCH_HOLE);
567const_assert_eq!(AllocateMode::COLLAPSE_RANGE.bits(), zxio::ZXIO_ALLOCATE_COLLAPSE_RANGE);
568const_assert_eq!(AllocateMode::ZERO_RANGE.bits(), zxio::ZXIO_ALLOCATE_ZERO_RANGE);
569const_assert_eq!(AllocateMode::INSERT_RANGE.bits(), zxio::ZXIO_ALLOCATE_INSERT_RANGE);
570
571#[derive(Default)]
575struct ZxioStorage {
576 storage: zxio::zxio_storage_t,
577 _pin: std::marker::PhantomPinned,
578}
579
580pub struct Zxio {
585 inner: Pin<Arc<ZxioStorage>>,
586}
587
588impl Default for Zxio {
589 fn default() -> Self {
590 Self { inner: Arc::pin(ZxioStorage::default()) }
591 }
592}
593
594impl std::fmt::Debug for Zxio {
595 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
596 f.debug_struct("Zxio").finish()
597 }
598}
599
600pub struct ZxioWeak(PinWeak<ZxioStorage>);
601
602impl ZxioWeak {
603 pub fn upgrade(&self) -> Option<Zxio> {
604 Some(Zxio { inner: self.0.upgrade()? })
605 }
606}
607
608pub trait ServiceConnector {
614 fn connect(service_name: &str) -> Result<&'static zx::Channel, zx::Status>;
616}
617
618unsafe extern "C" fn service_connector<S: ServiceConnector>(
624 service_name: *const c_char,
625 provider_handle: *mut zx_handle_t,
626) -> zx_status_t {
627 let status: zx::Status = (|| {
628 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
629 let service_name = unsafe { CStr::from_ptr(service_name) }
630 .to_str()
631 .map_err(|std::str::Utf8Error { .. }| zx::Status::INVALID_ARGS)?;
632
633 S::connect(service_name).map(|channel| {
634 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
635 unsafe {
636 *provider_handle = channel.raw_handle()
637 };
638 })
639 })()
640 .into();
641 status.into_raw()
642}
643
644unsafe extern "C" fn storage_allocator(
650 _type: zxio_object_type_t,
651 out_storage: *mut *mut zxio_storage_t,
652 out_context: *mut *mut c_void,
653) -> zx_status_t {
654 let zxio_ptr_ptr = out_context as *mut *mut zxio_storage_t;
655 let status: zx::Status = (|| {
656 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
657 if let Some(zxio_ptr) = unsafe { zxio_ptr_ptr.as_mut() } {
658 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
659 if let Some(zxio) = unsafe { zxio_ptr.as_mut() } {
660 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
661 unsafe {
662 *out_storage = zxio
663 };
664 return Ok(());
665 }
666 }
667 Err(zx::Status::NO_MEMORY)
668 })()
669 .into();
670 status.into_raw()
671}
672
673fn validate_pointer_fields(attrs: &zxio_node_attributes_t) {
677 assert!(
679 attrs.fsverity_root_hash.is_null(),
680 "Passed in a pointer for the fsverity_root_hash that could not assure the lifetime."
681 );
682 assert!(
683 attrs.selinux_context.is_null(),
684 "Passed in a pointer for the selinux_context that could not assure the lifetime."
685 );
686}
687
688fn clean_pointer_fields(attrs: &mut zxio_node_attributes_t) {
691 attrs.fsverity_root_hash = std::ptr::null_mut();
692 attrs.selinux_context = std::ptr::null_mut();
693}
694
695pub const ZXIO_ROOT_HASH_LENGTH: usize = 64;
696
697#[repr(transparent)]
699#[derive(IntoBytes, TryFromBytes, Immutable)]
700pub struct ZxioSocketMark(zxio_socket_mark_t);
701
702impl ZxioSocketMark {
703 fn new(domain: u8, value: u32) -> Self {
704 ZxioSocketMark(zxio_socket_mark_t { is_present: true, domain, value, ..Default::default() })
705 }
706
707 pub fn so_mark(mark: u32) -> Self {
709 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SO_MARK as u8, mark)
710 }
711
712 pub fn uid(uid: u32) -> Self {
714 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SOCKET_UID as u8, uid)
715 }
716}
717
718#[repr(transparent)]
720pub struct ZxioWakeGroupToken(zx_handle_t);
721
722impl ZxioWakeGroupToken {
723 pub fn new(token: Option<zx::Event>) -> Self {
725 ZxioWakeGroupToken(token.map(zx::Event::into_raw).unwrap_or(zx::sys::ZX_HANDLE_INVALID))
726 }
727}
728
729pub struct ZxioSocketCreationOptions<'a> {
731 pub marks: &'a mut [ZxioSocketMark],
732 pub wake_group: ZxioWakeGroupToken,
733}
734
735impl Zxio {
736 pub fn new_socket<S: ServiceConnector>(
737 domain: c_int,
738 socket_type: c_int,
739 protocol: c_int,
740 ZxioSocketCreationOptions { marks, wake_group }: ZxioSocketCreationOptions<'_>,
741 ) -> Result<Result<Self, ZxioErrorCode>, zx::Status> {
742 let zxio = Zxio::default();
743 let mut out_context = zxio.as_storage_ptr() as *mut c_void;
744 let mut out_code = 0;
745
746 let ZxioWakeGroupToken(wake_group) = wake_group;
747 let creation_opts = zxio::zxio_socket_creation_options {
748 num_marks: marks.len(),
749 marks: marks.as_mut_ptr() as *mut _,
750 wake_group,
751 ..Default::default()
752 };
753
754 #[allow(
755 clippy::undocumented_unsafe_blocks,
756 reason = "Force documented unsafe blocks in Starnix"
757 )]
758 let status = unsafe {
759 zxio::zxio_socket_with_options(
760 Some(service_connector::<S>),
761 domain,
762 socket_type,
763 protocol,
764 creation_opts,
765 Some(storage_allocator),
766 &mut out_context as *mut *mut c_void,
767 &mut out_code,
768 )
769 };
770 zx::ok(status)?;
771 match out_code {
772 0 => Ok(Ok(zxio)),
773 _ => Ok(Err(ZxioErrorCode(out_code))),
774 }
775 }
776
777 fn as_ptr(&self) -> *mut zxio::zxio_t {
778 &self.inner.storage.io as *const zxio::zxio_t as *mut zxio::zxio_t
779 }
780
781 fn as_storage_ptr(&self) -> *mut zxio::zxio_storage_t {
782 &self.inner.storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t
783 }
784
785 pub fn create(handle: zx::NullableHandle) -> Result<Zxio, zx::Status> {
786 let zxio = Zxio::default();
787 #[allow(
788 clippy::undocumented_unsafe_blocks,
789 reason = "Force documented unsafe blocks in Starnix"
790 )]
791 let status = unsafe { zxio::zxio_create(handle.into_raw(), zxio.as_storage_ptr()) };
792 zx::ok(status)?;
793 Ok(zxio)
794 }
795
796 pub fn release(self) -> Result<zx::NullableHandle, zx::Status> {
797 let mut handle = 0;
798 #[allow(
799 clippy::undocumented_unsafe_blocks,
800 reason = "Force documented unsafe blocks in Starnix"
801 )]
802 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut handle) };
803 zx::ok(status)?;
804 #[allow(
805 clippy::undocumented_unsafe_blocks,
806 reason = "Force documented unsafe blocks in Starnix"
807 )]
808 unsafe {
809 Ok(zx::NullableHandle::from_raw(handle))
810 }
811 }
812
813 pub fn open(
814 &self,
815 path: &str,
816 flags: fio::Flags,
817 mut options: ZxioOpenOptions<'_, '_>,
818 ) -> Result<Self, zx::Status> {
819 let zxio = Zxio::default();
820
821 let mut zxio_open_options = zxio::zxio_open_options::default();
822 zxio_open_options.inout_attr = match &mut options.attributes {
823 Some(a) => (*a) as *mut zxio_node_attributes_t,
824 None => std::ptr::null_mut(),
825 };
826 zxio_open_options.create_attr = match &options.create_attributes {
827 Some(a) => a as *const zxio_node_attributes_t,
828 None => std::ptr::null_mut(),
829 };
830
831 #[allow(
832 clippy::undocumented_unsafe_blocks,
833 reason = "Force documented unsafe blocks in Starnix"
834 )]
835 let status = unsafe {
836 zxio::zxio_open(
837 self.as_ptr(),
838 path.as_ptr() as *const c_char,
839 path.len(),
840 flags.bits(),
841 &zxio_open_options,
842 zxio.as_storage_ptr(),
843 )
844 };
845 options.init_context_from_read();
846 if let Some(attributes) = options.attributes {
847 clean_pointer_fields(attributes);
848 }
849 zx::ok(status)?;
850 Ok(zxio)
851 }
852
853 pub fn create_with_on_representation(
854 handle: zx::NullableHandle,
855 attributes: Option<&mut zxio_node_attributes_t>,
856 ) -> Result<Zxio, zx::Status> {
857 if let Some(attr) = &attributes {
858 validate_pointer_fields(attr);
859 }
860 let zxio = Zxio::default();
861 #[allow(
862 clippy::undocumented_unsafe_blocks,
863 reason = "Force documented unsafe blocks in Starnix"
864 )]
865 let status = unsafe {
866 zxio::zxio_create_with_on_representation(
867 handle.into_raw(),
868 attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
869 zxio.as_storage_ptr(),
870 )
871 };
872 zx::ok(status)?;
873 Ok(zxio)
874 }
875
876 pub fn open_node(
878 &self,
879 path: &str,
880 flags: fio::Flags,
881 attributes: Option<&mut zxio_node_attributes_t>,
882 ) -> Result<Self, zx::Status> {
883 let zxio = Zxio::default();
884
885 #[allow(
886 clippy::undocumented_unsafe_blocks,
887 reason = "Force documented unsafe blocks in Starnix"
888 )]
889 let status = unsafe {
890 zxio::zxio_open(
891 self.as_ptr(),
892 path.as_ptr() as *const c_char,
893 path.len(),
894 (flags | fio::Flags::PROTOCOL_NODE).bits(),
897 &zxio::zxio_open_options {
898 inout_attr: attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
899 ..Default::default()
900 },
901 zxio.as_storage_ptr(),
902 )
903 };
904 zx::ok(status)?;
905 Ok(zxio)
906 }
907
908 pub fn unlink(&self, name: &str, flags: fio::UnlinkFlags) -> Result<(), zx::Status> {
909 let flags_bits = flags.bits().try_into().map_err(|_| zx::Status::INVALID_ARGS)?;
910 #[allow(
911 clippy::undocumented_unsafe_blocks,
912 reason = "Force documented unsafe blocks in Starnix"
913 )]
914 let status = unsafe {
915 zxio::zxio_unlink(self.as_ptr(), name.as_ptr() as *const c_char, name.len(), flags_bits)
916 };
917 zx::ok(status)
918 }
919
920 pub fn read(&self, data: &mut [u8]) -> Result<usize, zx::Status> {
921 let flags = zxio::zxio_flags_t::default();
922 let mut actual = 0usize;
923 #[allow(
924 clippy::undocumented_unsafe_blocks,
925 reason = "Force documented unsafe blocks in Starnix"
926 )]
927 let status = unsafe {
928 zxio::zxio_read(
929 self.as_ptr(),
930 data.as_ptr() as *mut c_void,
931 data.len(),
932 flags,
933 &mut actual,
934 )
935 };
936 zx::ok(status)?;
937 Ok(actual)
938 }
939
940 pub unsafe fn readv(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
952 let flags = zxio::zxio_flags_t::default();
953 let mut actual = 0usize;
954 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
955 let status = unsafe {
956 zxio::zxio_readv(
957 self.as_ptr(),
958 data.as_ptr() as *const zxio::zx_iovec,
959 data.len(),
960 flags,
961 &mut actual,
962 )
963 };
964 zx::ok(status)?;
965 Ok(actual)
966 }
967
968 pub fn deep_clone(&self) -> Result<Zxio, zx::Status> {
969 Zxio::create(self.clone_handle()?)
970 }
971
972 pub fn clone_handle(&self) -> Result<zx::NullableHandle, zx::Status> {
973 let mut handle = 0;
974 #[allow(
975 clippy::undocumented_unsafe_blocks,
976 reason = "Force documented unsafe blocks in Starnix"
977 )]
978 let status = unsafe { zxio::zxio_clone(self.as_ptr(), &mut handle) };
979 zx::ok(status)?;
980 #[allow(
981 clippy::undocumented_unsafe_blocks,
982 reason = "Force documented unsafe blocks in Starnix"
983 )]
984 unsafe {
985 Ok(zx::NullableHandle::from_raw(handle))
986 }
987 }
988
989 pub fn downgrade(&self) -> ZxioWeak {
990 ZxioWeak(PinWeak::downgrade(self.inner.clone()))
991 }
992
993 pub fn read_at(&self, offset: u64, data: &mut [u8]) -> Result<usize, zx::Status> {
994 let flags = zxio::zxio_flags_t::default();
995 let mut actual = 0usize;
996 #[allow(
997 clippy::undocumented_unsafe_blocks,
998 reason = "Force documented unsafe blocks in Starnix"
999 )]
1000 let status = unsafe {
1001 zxio::zxio_read_at(
1002 self.as_ptr(),
1003 offset,
1004 data.as_ptr() as *mut c_void,
1005 data.len(),
1006 flags,
1007 &mut actual,
1008 )
1009 };
1010 zx::ok(status)?;
1011 Ok(actual)
1012 }
1013
1014 pub unsafe fn readv_at(
1027 &self,
1028 offset: u64,
1029 data: &[zxio::zx_iovec],
1030 ) -> Result<usize, zx::Status> {
1031 let flags = zxio::zxio_flags_t::default();
1032 let mut actual = 0usize;
1033 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1034 let status = unsafe {
1035 zxio::zxio_readv_at(
1036 self.as_ptr(),
1037 offset,
1038 data.as_ptr() as *const zxio::zx_iovec,
1039 data.len(),
1040 flags,
1041 &mut actual,
1042 )
1043 };
1044 zx::ok(status)?;
1045 Ok(actual)
1046 }
1047
1048 pub fn write(&self, data: &[u8]) -> Result<usize, zx::Status> {
1049 let flags = zxio::zxio_flags_t::default();
1050 let mut actual = 0;
1051 #[allow(
1052 clippy::undocumented_unsafe_blocks,
1053 reason = "Force documented unsafe blocks in Starnix"
1054 )]
1055 let status = unsafe {
1056 zxio::zxio_write(
1057 self.as_ptr(),
1058 data.as_ptr() as *const c_void,
1059 data.len(),
1060 flags,
1061 &mut actual,
1062 )
1063 };
1064 zx::ok(status)?;
1065 Ok(actual)
1066 }
1067
1068 pub unsafe fn writev(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
1081 let flags = zxio::zxio_flags_t::default();
1082 let mut actual = 0;
1083 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1084 let status = unsafe {
1085 zxio::zxio_writev(
1086 self.as_ptr(),
1087 data.as_ptr() as *const zxio::zx_iovec,
1088 data.len(),
1089 flags,
1090 &mut actual,
1091 )
1092 };
1093 zx::ok(status)?;
1094 Ok(actual)
1095 }
1096
1097 pub fn write_at(&self, offset: u64, data: &[u8]) -> Result<usize, zx::Status> {
1098 let flags = zxio::zxio_flags_t::default();
1099 let mut actual = 0;
1100 #[allow(
1101 clippy::undocumented_unsafe_blocks,
1102 reason = "Force documented unsafe blocks in Starnix"
1103 )]
1104 let status = unsafe {
1105 zxio::zxio_write_at(
1106 self.as_ptr(),
1107 offset,
1108 data.as_ptr() as *const c_void,
1109 data.len(),
1110 flags,
1111 &mut actual,
1112 )
1113 };
1114 zx::ok(status)?;
1115 Ok(actual)
1116 }
1117
1118 pub unsafe fn writev_at(
1131 &self,
1132 offset: u64,
1133 data: &[zxio::zx_iovec],
1134 ) -> Result<usize, zx::Status> {
1135 let flags = zxio::zxio_flags_t::default();
1136 let mut actual = 0;
1137 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1138 let status = unsafe {
1139 zxio::zxio_writev_at(
1140 self.as_ptr(),
1141 offset,
1142 data.as_ptr() as *const zxio::zx_iovec,
1143 data.len(),
1144 flags,
1145 &mut actual,
1146 )
1147 };
1148 zx::ok(status)?;
1149 Ok(actual)
1150 }
1151
1152 pub fn truncate(&self, length: u64) -> Result<(), zx::Status> {
1153 #[allow(
1154 clippy::undocumented_unsafe_blocks,
1155 reason = "Force documented unsafe blocks in Starnix"
1156 )]
1157 let status = unsafe { zxio::zxio_truncate(self.as_ptr(), length) };
1158 zx::ok(status)?;
1159 Ok(())
1160 }
1161
1162 pub fn seek(&self, seek_origin: SeekOrigin, offset: i64) -> Result<usize, zx::Status> {
1163 let mut result = 0;
1164 #[allow(
1165 clippy::undocumented_unsafe_blocks,
1166 reason = "Force documented unsafe blocks in Starnix"
1167 )]
1168 let status =
1169 unsafe { zxio::zxio_seek(self.as_ptr(), seek_origin.into(), offset, &mut result) };
1170 zx::ok(status)?;
1171 Ok(result)
1172 }
1173
1174 pub fn vmo_get(&self, flags: zx::VmarFlags) -> Result<zx::Vmo, zx::Status> {
1175 let mut vmo = 0;
1176 #[allow(
1177 clippy::undocumented_unsafe_blocks,
1178 reason = "Force documented unsafe blocks in Starnix"
1179 )]
1180 let status = unsafe { zxio::zxio_vmo_get(self.as_ptr(), flags.bits(), &mut vmo) };
1181 zx::ok(status)?;
1182 #[allow(
1183 clippy::undocumented_unsafe_blocks,
1184 reason = "Force documented unsafe blocks in Starnix"
1185 )]
1186 let handle = unsafe { zx::NullableHandle::from_raw(vmo) };
1187 Ok(zx::Vmo::from(handle))
1188 }
1189
1190 fn node_attributes_from_query(
1191 &self,
1192 query: zxio_node_attr_has_t,
1193 fsverity_root_hash: Option<&mut [u8; ZXIO_ROOT_HASH_LENGTH]>,
1194 ) -> zxio_node_attributes_t {
1195 if let Some(fsverity_root_hash) = fsverity_root_hash {
1196 zxio_node_attributes_t {
1197 has: query,
1198 fsverity_root_hash: fsverity_root_hash as *mut u8,
1199 ..Default::default()
1200 }
1201 } else {
1202 zxio_node_attributes_t { has: query, ..Default::default() }
1203 }
1204 }
1205
1206 pub fn attr_get(
1207 &self,
1208 query: zxio_node_attr_has_t,
1209 ) -> Result<zxio_node_attributes_t, zx::Status> {
1210 let mut attributes = self.node_attributes_from_query(query, None);
1211 #[allow(
1212 clippy::undocumented_unsafe_blocks,
1213 reason = "Force documented unsafe blocks in Starnix"
1214 )]
1215 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1216 zx::ok(status)?;
1217 Ok(attributes)
1218 }
1219
1220 pub fn close_and_update_access_time(self) -> Result<(), zx::Status> {
1226 let mut out_handle = zx::sys::ZX_HANDLE_INVALID;
1227 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut out_handle) };
1229 zx::ok(status)?;
1230 let proxy = fio::NodeSynchronousProxy::from_channel(
1231 unsafe { zx::NullableHandle::from_raw(out_handle) }.into(),
1233 );
1234
1235 let _ = proxy.get_attributes(
1240 fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
1241 zx::MonotonicInstant::INFINITE_PAST,
1242 );
1243
1244 Ok(())
1247 }
1248
1249 pub fn attr_get_with_root_hash(
1251 &self,
1252 query: zxio_node_attr_has_t,
1253 fsverity_root_hash: &mut [u8; ZXIO_ROOT_HASH_LENGTH],
1254 ) -> Result<zxio_node_attributes_t, zx::Status> {
1255 let mut attributes = self.node_attributes_from_query(query, Some(fsverity_root_hash));
1256 #[allow(
1257 clippy::undocumented_unsafe_blocks,
1258 reason = "Force documented unsafe blocks in Starnix"
1259 )]
1260 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1261 clean_pointer_fields(&mut attributes);
1262 zx::ok(status)?;
1263 Ok(attributes)
1264 }
1265
1266 pub fn attr_set(&self, attributes: &zxio_node_attributes_t) -> Result<(), zx::Status> {
1267 validate_pointer_fields(attributes);
1268 #[allow(
1269 clippy::undocumented_unsafe_blocks,
1270 reason = "Force documented unsafe blocks in Starnix"
1271 )]
1272 let status = unsafe { zxio::zxio_attr_set(self.as_ptr(), attributes) };
1273 zx::ok(status)?;
1274 Ok(())
1275 }
1276
1277 pub fn enable_verity(&self, descriptor: &zxio_fsverity_descriptor_t) -> Result<(), zx::Status> {
1278 #[allow(
1279 clippy::undocumented_unsafe_blocks,
1280 reason = "Force documented unsafe blocks in Starnix"
1281 )]
1282 let status = unsafe { zxio::zxio_enable_verity(self.as_ptr(), descriptor) };
1283 zx::ok(status)?;
1284 Ok(())
1285 }
1286
1287 pub fn rename(
1288 &self,
1289 old_path: &str,
1290 new_directory: &Zxio,
1291 new_path: &str,
1292 ) -> Result<(), zx::Status> {
1293 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1294 #[allow(
1295 clippy::undocumented_unsafe_blocks,
1296 reason = "Force documented unsafe blocks in Starnix"
1297 )]
1298 let status = unsafe { zxio::zxio_token_get(new_directory.as_ptr(), &mut handle) };
1299 zx::ok(status)?;
1300 #[allow(
1301 clippy::undocumented_unsafe_blocks,
1302 reason = "Force documented unsafe blocks in Starnix"
1303 )]
1304 let status = unsafe {
1305 zxio::zxio_rename(
1306 self.as_ptr(),
1307 old_path.as_ptr() as *const c_char,
1308 old_path.len(),
1309 handle,
1310 new_path.as_ptr() as *const c_char,
1311 new_path.len(),
1312 )
1313 };
1314 zx::ok(status)?;
1315 Ok(())
1316 }
1317
1318 pub fn wait_begin(
1319 &self,
1320 zxio_signals: zxio_signals_t,
1321 ) -> (zx::Unowned<'_, zx::NullableHandle>, zx::Signals) {
1322 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1323 let mut zx_signals = zx::sys::ZX_SIGNAL_NONE;
1324 #[allow(
1325 clippy::undocumented_unsafe_blocks,
1326 reason = "Force documented unsafe blocks in Starnix"
1327 )]
1328 unsafe {
1329 zxio::zxio_wait_begin(self.as_ptr(), zxio_signals, &mut handle, &mut zx_signals)
1330 };
1331 #[allow(
1332 clippy::undocumented_unsafe_blocks,
1333 reason = "Force documented unsafe blocks in Starnix"
1334 )]
1335 let handle = unsafe { zx::Unowned::<zx::NullableHandle>::from_raw_handle(handle) };
1336 let signals = zx::Signals::from_bits_truncate(zx_signals);
1337 (handle, signals)
1338 }
1339
1340 pub fn wait_end(&self, signals: zx::Signals) -> zxio_signals_t {
1341 let mut zxio_signals = ZxioSignals::NONE.bits();
1342 #[allow(
1343 clippy::undocumented_unsafe_blocks,
1344 reason = "Force documented unsafe blocks in Starnix"
1345 )]
1346 unsafe {
1347 zxio::zxio_wait_end(self.as_ptr(), signals.bits(), &mut zxio_signals);
1348 }
1349 zxio_signals
1350 }
1351
1352 pub fn create_dirent_iterator(&self) -> Result<DirentIterator<'_>, zx::Status> {
1353 let mut zxio_iterator = Box::default();
1354 #[allow(
1355 clippy::undocumented_unsafe_blocks,
1356 reason = "Force documented unsafe blocks in Starnix"
1357 )]
1358 let status = unsafe { zxio::zxio_dirent_iterator_init(&mut *zxio_iterator, self.as_ptr()) };
1359 zx::ok(status)?;
1360 let iterator =
1361 DirentIterator { iterator: zxio_iterator, _directory: PhantomData, finished: false };
1362 Ok(iterator)
1363 }
1364
1365 pub fn connect(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1366 let mut out_code = 0;
1367 #[allow(
1368 clippy::undocumented_unsafe_blocks,
1369 reason = "Force documented unsafe blocks in Starnix"
1370 )]
1371 let status = unsafe {
1372 zxio::zxio_connect(
1373 self.as_ptr(),
1374 addr.as_ptr() as *const sockaddr,
1375 addr.len() as socklen_t,
1376 &mut out_code,
1377 )
1378 };
1379 zx::ok(status)?;
1380 match out_code {
1381 0 => Ok(Ok(())),
1382 _ => Ok(Err(ZxioErrorCode(out_code))),
1383 }
1384 }
1385
1386 pub fn bind(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1387 let mut out_code = 0;
1388 #[allow(
1389 clippy::undocumented_unsafe_blocks,
1390 reason = "Force documented unsafe blocks in Starnix"
1391 )]
1392 let status = unsafe {
1393 zxio::zxio_bind(
1394 self.as_ptr(),
1395 addr.as_ptr() as *const sockaddr,
1396 addr.len() as socklen_t,
1397 &mut out_code,
1398 )
1399 };
1400 zx::ok(status)?;
1401 match out_code {
1402 0 => Ok(Ok(())),
1403 _ => Ok(Err(ZxioErrorCode(out_code))),
1404 }
1405 }
1406
1407 pub fn listen(&self, backlog: i32) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1408 let mut out_code = 0;
1409 #[allow(
1410 clippy::undocumented_unsafe_blocks,
1411 reason = "Force documented unsafe blocks in Starnix"
1412 )]
1413 let status = unsafe { zxio::zxio_listen(self.as_ptr(), backlog as c_int, &mut out_code) };
1414 zx::ok(status)?;
1415 match out_code {
1416 0 => Ok(Ok(())),
1417 _ => Ok(Err(ZxioErrorCode(out_code))),
1418 }
1419 }
1420
1421 pub fn accept(&self) -> Result<Result<Zxio, ZxioErrorCode>, zx::Status> {
1422 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1423 let mut addr = vec![0u8; addrlen as usize];
1424 let zxio = Zxio::default();
1425 let mut out_code = 0;
1426 #[allow(
1427 clippy::undocumented_unsafe_blocks,
1428 reason = "Force documented unsafe blocks in Starnix"
1429 )]
1430 let status = unsafe {
1431 zxio::zxio_accept(
1432 self.as_ptr(),
1433 addr.as_mut_ptr() as *mut sockaddr,
1434 &mut addrlen,
1435 zxio.as_storage_ptr(),
1436 &mut out_code,
1437 )
1438 };
1439 zx::ok(status)?;
1440 match out_code {
1441 0 => Ok(Ok(zxio)),
1442 _ => Ok(Err(ZxioErrorCode(out_code))),
1443 }
1444 }
1445
1446 pub fn getsockname(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1447 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1448 let mut addr = vec![0u8; addrlen as usize];
1449 let mut out_code = 0;
1450 #[allow(
1451 clippy::undocumented_unsafe_blocks,
1452 reason = "Force documented unsafe blocks in Starnix"
1453 )]
1454 let status = unsafe {
1455 zxio::zxio_getsockname(
1456 self.as_ptr(),
1457 addr.as_mut_ptr() as *mut sockaddr,
1458 &mut addrlen,
1459 &mut out_code,
1460 )
1461 };
1462 zx::ok(status)?;
1463 match out_code {
1464 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1465 _ => Ok(Err(ZxioErrorCode(out_code))),
1466 }
1467 }
1468
1469 pub fn getpeername(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1470 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1471 let mut addr = vec![0u8; addrlen as usize];
1472 let mut out_code = 0;
1473 #[allow(
1474 clippy::undocumented_unsafe_blocks,
1475 reason = "Force documented unsafe blocks in Starnix"
1476 )]
1477 let status = unsafe {
1478 zxio::zxio_getpeername(
1479 self.as_ptr(),
1480 addr.as_mut_ptr() as *mut sockaddr,
1481 &mut addrlen,
1482 &mut out_code,
1483 )
1484 };
1485 zx::ok(status)?;
1486 match out_code {
1487 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1488 _ => Ok(Err(ZxioErrorCode(out_code))),
1489 }
1490 }
1491
1492 pub fn getsockopt_slice(
1493 &self,
1494 level: u32,
1495 optname: u32,
1496 optval: &mut [u8],
1497 ) -> Result<Result<socklen_t, ZxioErrorCode>, zx::Status> {
1498 let mut optlen = optval.len() as socklen_t;
1499 let mut out_code = 0;
1500 #[allow(
1501 clippy::undocumented_unsafe_blocks,
1502 reason = "Force documented unsafe blocks in Starnix"
1503 )]
1504 let status = unsafe {
1505 zxio::zxio_getsockopt(
1506 self.as_ptr(),
1507 level as c_int,
1508 optname as c_int,
1509 optval.as_mut_ptr() as *mut c_void,
1510 &mut optlen,
1511 &mut out_code,
1512 )
1513 };
1514 zx::ok(status)?;
1515 match out_code {
1516 0 => Ok(Ok(optlen)),
1517 _ => Ok(Err(ZxioErrorCode(out_code))),
1518 }
1519 }
1520
1521 pub fn getsockopt(
1522 &self,
1523 level: u32,
1524 optname: u32,
1525 optlen: socklen_t,
1526 ) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1527 let mut optval = vec![0u8; optlen as usize];
1528 let result = self.getsockopt_slice(level, optname, &mut optval[..])?;
1529 Ok(result.map(|optlen| optval[..optlen as usize].to_vec()))
1530 }
1531
1532 pub fn setsockopt(
1533 &self,
1534 level: i32,
1535 optname: i32,
1536 optval: &[u8],
1537 access_token: Option<zx::NullableHandle>,
1538 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1539 let mut out_code = 0;
1540 let access_token_handle =
1541 access_token.map(zx::NullableHandle::into_raw).unwrap_or(zx::sys::ZX_HANDLE_INVALID);
1542
1543 #[allow(
1544 clippy::undocumented_unsafe_blocks,
1545 reason = "Force documented unsafe blocks in Starnix"
1546 )]
1547 let status = unsafe {
1548 zxio::zxio_setsockopt(
1549 self.as_ptr(),
1550 level,
1551 optname,
1552 optval.as_ptr() as *const c_void,
1553 optval.len() as socklen_t,
1554 access_token_handle,
1555 &mut out_code,
1556 )
1557 };
1558 zx::ok(status)?;
1559 match out_code {
1560 0 => Ok(Ok(())),
1561 _ => Ok(Err(ZxioErrorCode(out_code))),
1562 }
1563 }
1564
1565 pub fn shutdown(
1566 &self,
1567 flags: ZxioShutdownFlags,
1568 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1569 let mut out_code = 0;
1570 #[allow(
1571 clippy::undocumented_unsafe_blocks,
1572 reason = "Force documented unsafe blocks in Starnix"
1573 )]
1574 let status = unsafe { zxio::zxio_shutdown(self.as_ptr(), flags.bits(), &mut out_code) };
1575 zx::ok(status)?;
1576 match out_code {
1577 0 => Ok(Ok(())),
1578 _ => Ok(Err(ZxioErrorCode(out_code))),
1579 }
1580 }
1581
1582 pub fn sendmsg(
1583 &self,
1584 addr: &mut [u8],
1585 buffer: &mut [zxio::iovec],
1586 cmsg: &[ControlMessage],
1587 flags: u32,
1588 ) -> Result<Result<usize, ZxioErrorCode>, zx::Status> {
1589 let mut msg = zxio::msghdr::default();
1590 msg.msg_name = match addr.len() {
1591 0 => std::ptr::null_mut() as *mut c_void,
1592 _ => addr.as_mut_ptr() as *mut c_void,
1593 };
1594 msg.msg_namelen = addr.len() as u32;
1595
1596 msg.msg_iovlen =
1597 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1598 msg.msg_iov = buffer.as_mut_ptr();
1599
1600 let mut cmsg_buffer = serialize_control_messages(cmsg);
1601 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1602 msg.msg_controllen = cmsg_buffer.len() as u32;
1603
1604 let mut out_code = 0;
1605 let mut out_actual = 0;
1606
1607 #[allow(
1608 clippy::undocumented_unsafe_blocks,
1609 reason = "Force documented unsafe blocks in Starnix"
1610 )]
1611 let status = unsafe {
1612 zxio::zxio_sendmsg(self.as_ptr(), &msg, flags as c_int, &mut out_actual, &mut out_code)
1613 };
1614
1615 zx::ok(status)?;
1616 match out_code {
1617 0 => Ok(Ok(out_actual)),
1618 _ => Ok(Err(ZxioErrorCode(out_code))),
1619 }
1620 }
1621
1622 pub fn recvmsg(
1623 &self,
1624 buffer: &mut [zxio::iovec],
1625 flags: u32,
1626 ) -> Result<Result<RecvMessageInfo, ZxioErrorCode>, zx::Status> {
1627 let mut msg = msghdr::default();
1628 let mut addr = vec![0u8; std::mem::size_of::<sockaddr_storage>()];
1629 msg.msg_name = addr.as_mut_ptr() as *mut c_void;
1630 msg.msg_namelen = addr.len() as u32;
1631
1632 let max_buffer_capacity = buffer.iter().map(|v| v.iov_len).sum();
1633 msg.msg_iovlen =
1634 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1635 msg.msg_iov = buffer.as_mut_ptr();
1636
1637 let mut cmsg_buffer = vec![0u8; MAX_CMSGS_BUFFER];
1638 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1639 msg.msg_controllen = cmsg_buffer.len() as u32;
1640
1641 let mut out_code = 0;
1642 let mut out_actual = 0;
1643 #[allow(
1644 clippy::undocumented_unsafe_blocks,
1645 reason = "Force documented unsafe blocks in Starnix"
1646 )]
1647 let status = unsafe {
1648 zxio::zxio_recvmsg(
1649 self.as_ptr(),
1650 &mut msg,
1651 flags as c_int,
1652 &mut out_actual,
1653 &mut out_code,
1654 )
1655 };
1656 zx::ok(status)?;
1657
1658 if out_code != 0 {
1659 return Ok(Err(ZxioErrorCode(out_code)));
1660 }
1661
1662 let control_messages = parse_control_messages(&cmsg_buffer[..msg.msg_controllen as usize]);
1663 Ok(Ok(RecvMessageInfo {
1664 address: addr[..msg.msg_namelen as usize].to_vec(),
1665 bytes_read: std::cmp::min(max_buffer_capacity, out_actual),
1666 message_length: out_actual,
1667 control_messages,
1668 flags: msg.msg_flags,
1669 }))
1670 }
1671
1672 pub fn read_link(&self) -> Result<&[u8], zx::Status> {
1673 let mut target = std::ptr::null();
1674 let mut target_len = 0;
1675 #[allow(
1676 clippy::undocumented_unsafe_blocks,
1677 reason = "Force documented unsafe blocks in Starnix"
1678 )]
1679 let status = unsafe { zxio::zxio_read_link(self.as_ptr(), &mut target, &mut target_len) };
1680 zx::ok(status)?;
1681 unsafe { Ok(std::slice::from_raw_parts(target, target_len)) }
1683 }
1684
1685 pub fn create_symlink(&self, name: &str, target: &[u8]) -> Result<Zxio, zx::Status> {
1686 let name = name.as_bytes();
1687 let zxio = Zxio::default();
1688 #[allow(
1689 clippy::undocumented_unsafe_blocks,
1690 reason = "Force documented unsafe blocks in Starnix"
1691 )]
1692 let status = unsafe {
1693 zxio::zxio_create_symlink(
1694 self.as_ptr(),
1695 name.as_ptr() as *const c_char,
1696 name.len(),
1697 target.as_ptr(),
1698 target.len(),
1699 zxio.as_storage_ptr(),
1700 )
1701 };
1702 zx::ok(status)?;
1703 Ok(zxio)
1704 }
1705
1706 pub fn xattr_list(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
1707 unsafe extern "C" fn callback(context: *mut c_void, name: *const u8, name_len: usize) {
1708 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1709 let out_names = unsafe { &mut *(context as *mut Vec<Vec<u8>>) };
1710 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1711 let name_slice = unsafe { std::slice::from_raw_parts(name, name_len) };
1712 out_names.push(name_slice.to_vec());
1713 }
1714 let mut out_names = Vec::new();
1715 #[allow(
1716 clippy::undocumented_unsafe_blocks,
1717 reason = "Force documented unsafe blocks in Starnix"
1718 )]
1719 let status = unsafe {
1720 zxio::zxio_xattr_list(
1721 self.as_ptr(),
1722 Some(callback),
1723 &mut out_names as *mut _ as *mut c_void,
1724 )
1725 };
1726 zx::ok(status)?;
1727 Ok(out_names)
1728 }
1729
1730 pub fn xattr_get(&self, name: &[u8]) -> Result<Vec<u8>, zx::Status> {
1731 unsafe extern "C" fn callback(
1732 context: *mut c_void,
1733 data: zxio::zxio_xattr_data_t,
1734 ) -> zx_status_t {
1735 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1736 let out_value = unsafe { &mut *(context as *mut Vec<u8>) };
1737 if data.data.is_null() {
1738 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1739 let value_vmo = unsafe { zx::Unowned::<'_, zx::Vmo>::from_raw_handle(data.vmo) };
1740 match value_vmo.read_to_vec(0, data.len as u64) {
1741 Ok(vec) => *out_value = vec,
1742 Err(status) => return status.into_raw(),
1743 }
1744 } else {
1745 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1746 let value_slice =
1747 unsafe { std::slice::from_raw_parts(data.data as *mut u8, data.len) };
1748 out_value.extend_from_slice(value_slice);
1749 }
1750 zx::Status::OK.into_raw()
1751 }
1752 let mut out_value = Vec::new();
1753 #[allow(
1754 clippy::undocumented_unsafe_blocks,
1755 reason = "Force documented unsafe blocks in Starnix"
1756 )]
1757 let status = unsafe {
1758 zxio::zxio_xattr_get(
1759 self.as_ptr(),
1760 name.as_ptr(),
1761 name.len(),
1762 Some(callback),
1763 &mut out_value as *mut _ as *mut c_void,
1764 )
1765 };
1766 zx::ok(status)?;
1767 Ok(out_value)
1768 }
1769
1770 pub fn xattr_set(
1771 &self,
1772 name: &[u8],
1773 value: &[u8],
1774 mode: XattrSetMode,
1775 ) -> Result<(), zx::Status> {
1776 #[allow(
1777 clippy::undocumented_unsafe_blocks,
1778 reason = "Force documented unsafe blocks in Starnix"
1779 )]
1780 let status = unsafe {
1781 zxio::zxio_xattr_set(
1782 self.as_ptr(),
1783 name.as_ptr(),
1784 name.len(),
1785 value.as_ptr(),
1786 value.len(),
1787 mode as u32,
1788 )
1789 };
1790 zx::ok(status)
1791 }
1792
1793 pub fn xattr_remove(&self, name: &[u8]) -> Result<(), zx::Status> {
1794 #[allow(
1795 clippy::undocumented_unsafe_blocks,
1796 reason = "Force documented unsafe blocks in Starnix"
1797 )]
1798 zx::ok(unsafe { zxio::zxio_xattr_remove(self.as_ptr(), name.as_ptr(), name.len()) })
1799 }
1800
1801 pub fn link_into(&self, target_dir: &Zxio, name: &str) -> Result<(), zx::Status> {
1802 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1803 #[allow(
1804 clippy::undocumented_unsafe_blocks,
1805 reason = "Force documented unsafe blocks in Starnix"
1806 )]
1807 zx::ok(unsafe { zxio::zxio_token_get(target_dir.as_ptr(), &mut handle) })?;
1808 #[allow(
1809 clippy::undocumented_unsafe_blocks,
1810 reason = "Force documented unsafe blocks in Starnix"
1811 )]
1812 zx::ok(unsafe {
1813 zxio::zxio_link_into(self.as_ptr(), handle, name.as_ptr() as *const c_char, name.len())
1814 })
1815 }
1816
1817 pub fn allocate(&self, offset: u64, len: u64, mode: AllocateMode) -> Result<(), zx::Status> {
1818 #[allow(
1819 clippy::undocumented_unsafe_blocks,
1820 reason = "Force documented unsafe blocks in Starnix"
1821 )]
1822 let status = unsafe { zxio::zxio_allocate(self.as_ptr(), offset, len, mode.bits()) };
1823 zx::ok(status)
1824 }
1825
1826 pub fn sync(&self) -> Result<(), zx::Status> {
1827 #[allow(
1828 clippy::undocumented_unsafe_blocks,
1829 reason = "Force documented unsafe blocks in Starnix"
1830 )]
1831 let status = unsafe { zxio::zxio_sync(self.as_ptr()) };
1832 zx::ok(status)
1833 }
1834
1835 pub fn close(&self) -> Result<(), zx::Status> {
1836 #[allow(
1837 clippy::undocumented_unsafe_blocks,
1838 reason = "Force documented unsafe blocks in Starnix"
1839 )]
1840 let status = unsafe { zxio::zxio_close(self.as_ptr()) };
1841 zx::ok(status)
1842 }
1843
1844 pub fn get_read_buffer_available(&self) -> Result<usize, zx::Status> {
1845 let mut available = 0usize;
1846 let status = unsafe { zxio::zxio_get_read_buffer_available(self.as_ptr(), &mut available) };
1849 zx::ok(status)?;
1850 Ok(available)
1851 }
1852}
1853
1854impl Drop for ZxioStorage {
1855 fn drop(&mut self) {
1856 let zxio_ptr: *mut zxio::zxio_t = &mut self.storage.io;
1861 #[allow(
1862 clippy::undocumented_unsafe_blocks,
1863 reason = "Force documented unsafe blocks in Starnix"
1864 )]
1865 unsafe {
1866 zxio::zxio_destroy(zxio_ptr);
1867 };
1868 }
1869}
1870
1871impl Clone for Zxio {
1872 fn clone(&self) -> Self {
1873 Self { inner: self.inner.clone() }
1874 }
1875}
1876
1877enum NodeKind {
1878 File,
1879 Directory,
1880 Symlink,
1881 Unknown,
1882}
1883
1884impl From<fio::Representation> for NodeKind {
1885 fn from(representation: fio::Representation) -> Self {
1886 match representation {
1887 fio::Representation::File(_) => NodeKind::File,
1888 fio::Representation::Directory(_) => NodeKind::Directory,
1889 fio::Representation::Symlink(_) => NodeKind::Symlink,
1890 _ => NodeKind::Unknown,
1891 }
1892 }
1893}
1894
1895struct DescribedNode {
1900 node: fio::NodeSynchronousProxy,
1901 kind: NodeKind,
1902}
1903
1904fn directory_open(
1914 directory: &fio::DirectorySynchronousProxy,
1915 path: &str,
1916 flags: fio::Flags,
1917 deadline: zx::MonotonicInstant,
1918) -> Result<DescribedNode, zx::Status> {
1919 let flags = flags | fio::Flags::FLAG_SEND_REPRESENTATION;
1920
1921 let (client_end, server_end) = zx::Channel::create();
1922 directory.open(path, flags, &Default::default(), server_end).map_err(|_| zx::Status::IO)?;
1923 let node = fio::NodeSynchronousProxy::new(client_end);
1924
1925 match node.wait_for_event(deadline).map_err(map_fidl_error)? {
1926 fio::NodeEvent::OnOpen_ { .. } => {
1927 panic!("Should never happen when sending FLAG_SEND_REPRESENTATION")
1928 }
1929 fio::NodeEvent::OnRepresentation { payload } => {
1930 Ok(DescribedNode { node, kind: payload.into() })
1931 }
1932 fio::NodeEvent::_UnknownEvent { .. } => Err(zx::Status::NOT_SUPPORTED),
1933 }
1934}
1935
1936pub fn directory_open_vmo(
1944 directory: &fio::DirectorySynchronousProxy,
1945 path: &str,
1946 vmo_flags: fio::VmoFlags,
1947 deadline: zx::MonotonicInstant,
1948) -> Result<zx::Vmo, zx::Status> {
1949 let mut flags = fio::Flags::empty();
1950 if vmo_flags.contains(fio::VmoFlags::READ) {
1951 flags |= fio::PERM_READABLE;
1952 }
1953 if vmo_flags.contains(fio::VmoFlags::WRITE) {
1954 flags |= fio::PERM_WRITABLE;
1955 }
1956 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
1957 flags |= fio::PERM_EXECUTABLE;
1958 }
1959 let description = directory_open(directory, path, flags, deadline)?;
1960 let file = match description.kind {
1961 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
1962 _ => return Err(zx::Status::IO),
1963 };
1964
1965 let vmo = file
1966 .get_backing_memory(vmo_flags, deadline)
1967 .map_err(map_fidl_error)?
1968 .map_err(zx::Status::from_raw)?;
1969 Ok(vmo)
1970}
1971
1972pub fn directory_read_file(
1977 directory: &fio::DirectorySynchronousProxy,
1978 path: &str,
1979 deadline: zx::MonotonicInstant,
1980) -> Result<Vec<u8>, zx::Status> {
1981 let description = directory_open(directory, path, fio::PERM_READABLE, 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 mut result = Vec::new();
1988 loop {
1989 let mut data = file
1990 .read(fio::MAX_TRANSFER_SIZE, deadline)
1991 .map_err(map_fidl_error)?
1992 .map_err(zx::Status::from_raw)?;
1993 let finished = (data.len() as u64) < fio::MAX_TRANSFER_SIZE;
1994 result.append(&mut data);
1995 if finished {
1996 return Ok(result);
1997 }
1998 }
1999}
2000
2001pub fn directory_create_tmp_file(
2003 directory: &fio::DirectorySynchronousProxy,
2004 flags: fio::Flags,
2005 deadline: zx::MonotonicInstant,
2006) -> Result<fio::FileSynchronousProxy, zx::Status> {
2007 let description = directory_open(
2008 directory,
2009 ".",
2010 flags
2011 | fio::PERM_WRITABLE
2012 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2013 | fio::Flags::PROTOCOL_FILE,
2014 deadline,
2015 )?;
2016 let file = match description.kind {
2017 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
2018 _ => return Err(zx::Status::NOT_FILE),
2019 };
2020
2021 Ok(file)
2022}
2023
2024pub fn directory_open_async(
2034 directory: &fio::DirectorySynchronousProxy,
2035 path: &str,
2036 flags: fio::Flags,
2037) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2038 if flags.intersects(fio::Flags::FLAG_SEND_REPRESENTATION) {
2039 return Err(zx::Status::INVALID_ARGS);
2040 }
2041
2042 let (proxy, server_end) = fidl::endpoints::create_sync_proxy::<fio::DirectoryMarker>();
2043 directory
2044 .open(path, flags, &Default::default(), server_end.into_channel())
2045 .map_err(|_| zx::Status::IO)?;
2046 Ok(proxy)
2047}
2048
2049pub fn directory_open_directory_async(
2060 directory: &fio::DirectorySynchronousProxy,
2061 path: &str,
2062 flags: fio::Flags,
2063) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2064 let flags = flags | fio::Flags::PROTOCOL_DIRECTORY;
2065 let proxy = directory_open_async(directory, path, flags)?;
2066 Ok(proxy)
2067}
2068
2069fn map_fidl_error(error: fidl::Error) -> zx::Status {
2070 match error {
2071 fidl::Error::ClientChannelClosed { epitaph, .. } => match epitaph.into() {
2072 Err(s) => s,
2073 Ok(()) => zx::Status::PEER_CLOSED,
2074 },
2075 _ => zx::Status::IO,
2076 }
2077}
2078
2079#[cfg(test)]
2080mod test {
2081 use super::*;
2082
2083 use anyhow::Error;
2084 use fidl::endpoints::Proxy as _;
2085 use fidl_fuchsia_io as fio;
2086 use fuchsia_fs::directory;
2087
2088 fn open_pkg() -> fio::DirectorySynchronousProxy {
2089 let pkg_proxy =
2090 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2091 .expect("failed to open /pkg");
2092 fio::DirectorySynchronousProxy::new(
2093 pkg_proxy
2094 .into_channel()
2095 .expect("failed to convert proxy into channel")
2096 .into_zx_channel(),
2097 )
2098 }
2099
2100 #[fuchsia::test]
2101 async fn test_directory_open() -> Result<(), Error> {
2102 let pkg = open_pkg();
2103 let description = directory_open(
2104 &pkg,
2105 "bin/syncio_lib_test",
2106 fio::PERM_READABLE,
2107 zx::MonotonicInstant::INFINITE,
2108 )?;
2109 assert!(match description.kind {
2110 NodeKind::File => true,
2111 _ => false,
2112 });
2113 Ok(())
2114 }
2115
2116 #[fuchsia::test]
2117 async fn test_directory_open_vmo() -> Result<(), Error> {
2118 let pkg = open_pkg();
2119 let vmo = directory_open_vmo(
2120 &pkg,
2121 "bin/syncio_lib_test",
2122 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2123 zx::MonotonicInstant::INFINITE,
2124 )?;
2125 assert!(!vmo.is_invalid());
2126
2127 let info = vmo.basic_info()?;
2128 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2129 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2130 Ok(())
2131 }
2132
2133 #[fuchsia::test]
2134 async fn test_directory_read_file() -> Result<(), Error> {
2135 let pkg = open_pkg();
2136 let data =
2137 directory_read_file(&pkg, "bin/syncio_lib_test", zx::MonotonicInstant::INFINITE)?;
2138
2139 assert!(!data.is_empty());
2140 Ok(())
2141 }
2142
2143 #[fuchsia::test]
2144 async fn test_directory_open_directory_async() -> Result<(), Error> {
2145 let pkg = open_pkg();
2146 let bin =
2147 directory_open_directory_async(&pkg, "bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE)?;
2148 let vmo = directory_open_vmo(
2149 &bin,
2150 "syncio_lib_test",
2151 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2152 zx::MonotonicInstant::INFINITE,
2153 )?;
2154 assert!(!vmo.is_invalid());
2155
2156 let info = vmo.basic_info()?;
2157 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2158 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2159 Ok(())
2160 }
2161
2162 #[fuchsia::test]
2163 async fn test_directory_open_zxio_async() -> Result<(), Error> {
2164 let pkg_proxy =
2165 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2166 .expect("failed to open /pkg");
2167 let zx_channel = pkg_proxy
2168 .into_channel()
2169 .expect("failed to convert proxy into channel")
2170 .into_zx_channel();
2171 let storage = zxio::zxio_storage_t::default();
2172 #[allow(
2173 clippy::undocumented_unsafe_blocks,
2174 reason = "Force documented unsafe blocks in Starnix"
2175 )]
2176 let status = unsafe {
2177 zxio::zxio_create(
2178 zx_channel.into_raw(),
2179 &storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t,
2180 )
2181 };
2182 assert_eq!(status, zx::sys::ZX_OK);
2183 let io = &storage.io as *const zxio::zxio_t as *mut zxio::zxio_t;
2184 #[allow(
2185 clippy::undocumented_unsafe_blocks,
2186 reason = "Force documented unsafe blocks in Starnix"
2187 )]
2188 let close_status = unsafe { zxio::zxio_close(io) };
2189 assert_eq!(close_status, zx::sys::ZX_OK);
2190 #[allow(
2191 clippy::undocumented_unsafe_blocks,
2192 reason = "Force documented unsafe blocks in Starnix"
2193 )]
2194 unsafe {
2195 zxio::zxio_destroy(io);
2196 }
2197 Ok(())
2198 }
2199
2200 #[fuchsia::test]
2201 async fn test_directory_enumerate() -> Result<(), Error> {
2202 let pkg_dir_handle =
2203 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2204 .expect("failed to open /pkg")
2205 .into_channel()
2206 .expect("could not unwrap channel")
2207 .into_zx_channel()
2208 .into();
2209
2210 let io: Zxio = Zxio::create(pkg_dir_handle)?;
2211 let iter = io.create_dirent_iterator().expect("failed to create iterator");
2212 let expected_dir_names = vec![".", "bin", "lib", "meta"];
2213 let mut found_dir_names = iter
2214 .map(|e| {
2215 let dirent = e.expect("dirent");
2216 assert!(dirent.is_dir());
2217 std::str::from_utf8(&dirent.name).expect("name was not valid utf8").to_string()
2218 })
2219 .collect::<Vec<_>>();
2220 found_dir_names.sort();
2221 assert_eq!(expected_dir_names, found_dir_names);
2222
2223 let bin_io = io
2225 .open("bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE, Default::default())
2226 .expect("open");
2227 for entry in bin_io.create_dirent_iterator().expect("failed to create iterator") {
2228 let dirent = entry.expect("dirent");
2229 if dirent.name == "." {
2230 assert!(dirent.is_dir());
2231 } else {
2232 assert!(dirent.is_file());
2233 }
2234 }
2235
2236 Ok(())
2237 }
2238
2239 #[fuchsia::test]
2240 fn test_storage_allocator() {
2241 let mut out_storage = zxio_storage_t::default();
2242 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2243
2244 let mut out_context = Zxio::default();
2245 let mut out_context_ptr = &mut out_context as *mut Zxio;
2246
2247 #[allow(
2248 clippy::undocumented_unsafe_blocks,
2249 reason = "Force documented unsafe blocks in Starnix"
2250 )]
2251 let out = unsafe {
2252 storage_allocator(
2253 0 as zxio_object_type_t,
2254 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2255 &mut out_context_ptr as *mut *mut Zxio as *mut *mut c_void,
2256 )
2257 };
2258 assert_eq!(out, zx::sys::ZX_OK);
2259 }
2260
2261 #[fuchsia::test]
2262 fn test_storage_allocator_bad_context() {
2263 let mut out_storage = zxio_storage_t::default();
2264 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2265
2266 let out_context = std::ptr::null_mut();
2267
2268 #[allow(
2269 clippy::undocumented_unsafe_blocks,
2270 reason = "Force documented unsafe blocks in Starnix"
2271 )]
2272 let out = unsafe {
2273 storage_allocator(
2274 0 as zxio_object_type_t,
2275 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2276 out_context,
2277 )
2278 };
2279 assert_eq!(out, zx::sys::ZX_ERR_NO_MEMORY);
2280 }
2281
2282 #[fuchsia::test]
2283 fn test_parse_ip_pktinfo_control_message() {
2284 let pktinfo = zxio::in_pktinfo {
2285 ipi_ifindex: 1,
2286 ipi_spec_dst: zxio::in_addr { s_addr: u32::from_ne_bytes([192, 0, 2, 1]) },
2287 ipi_addr: zxio::in_addr { s_addr: u32::from_ne_bytes([192, 0, 2, 2]) },
2288 };
2289 let total_size = CMSG_HEADER_SIZE + size_of_val(&pktinfo);
2290 let header = zxio::cmsghdr {
2291 cmsg_len: total_size as c_uint,
2292 cmsg_level: zxio::SOL_IP as i32,
2293 cmsg_type: zxio::IP_PKTINFO as i32,
2294 };
2295 let mut out = vec![0u8; total_size];
2296 header.write_to_prefix(&mut out[..]).unwrap();
2297 pktinfo.write_to_prefix(&mut out[CMSG_HEADER_SIZE..]).unwrap();
2298 let parsed = parse_control_messages(&out);
2299 assert_eq!(
2300 parsed,
2301 vec![ControlMessage::IpPacketInfo {
2302 iface: 1,
2303 local_addr: [192, 0, 2, 1],
2304 header_destination_addr: [192, 0, 2, 2],
2305 }]
2306 );
2307 }
2308}