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 zx::Status::result_into_raw((|| {
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}
641
642unsafe extern "C" fn storage_allocator(
648 _type: zxio_object_type_t,
649 out_storage: *mut *mut zxio_storage_t,
650 out_context: *mut *mut c_void,
651) -> zx_status_t {
652 let zxio_ptr_ptr = out_context as *mut *mut zxio_storage_t;
653 zx::Status::result_into_raw((|| {
654 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
655 if let Some(zxio_ptr) = unsafe { zxio_ptr_ptr.as_mut() } {
656 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
657 if let Some(zxio) = unsafe { zxio_ptr.as_mut() } {
658 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
659 unsafe {
660 *out_storage = zxio
661 };
662 return Ok(());
663 }
664 }
665 Err(zx::Status::NO_MEMORY)
666 })())
667}
668
669fn validate_pointer_fields(attrs: &zxio_node_attributes_t) {
673 assert!(
675 attrs.fsverity_root_hash.is_null(),
676 "Passed in a pointer for the fsverity_root_hash that could not assure the lifetime."
677 );
678 assert!(
679 attrs.selinux_context.is_null(),
680 "Passed in a pointer for the selinux_context that could not assure the lifetime."
681 );
682}
683
684fn clean_pointer_fields(attrs: &mut zxio_node_attributes_t) {
687 attrs.fsverity_root_hash = std::ptr::null_mut();
688 attrs.selinux_context = std::ptr::null_mut();
689}
690
691pub const ZXIO_ROOT_HASH_LENGTH: usize = 64;
692
693#[repr(transparent)]
695#[derive(IntoBytes, TryFromBytes, Immutable)]
696pub struct ZxioSocketMark(zxio_socket_mark_t);
697
698impl ZxioSocketMark {
699 fn new(domain: u8, value: u32) -> Self {
700 ZxioSocketMark(zxio_socket_mark_t { is_present: true, domain, value, ..Default::default() })
701 }
702
703 pub fn so_mark(mark: u32) -> Self {
705 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SO_MARK as u8, mark)
706 }
707
708 pub fn uid(uid: u32) -> Self {
710 Self::new(fidl_fuchsia_net::MARK_DOMAIN_SOCKET_UID as u8, uid)
711 }
712}
713
714#[repr(transparent)]
716pub struct ZxioWakeGroupToken(zx_handle_t);
717
718impl ZxioWakeGroupToken {
719 pub fn new(token: Option<zx::Event>) -> Self {
721 ZxioWakeGroupToken(token.map(zx::Event::into_raw).unwrap_or(zx::sys::ZX_HANDLE_INVALID))
722 }
723}
724
725pub struct ZxioSocketCreationOptions<'a> {
727 pub marks: &'a mut [ZxioSocketMark],
728 pub wake_group: ZxioWakeGroupToken,
729}
730
731impl Zxio {
732 pub fn new_socket<S: ServiceConnector>(
733 domain: c_int,
734 socket_type: c_int,
735 protocol: c_int,
736 ZxioSocketCreationOptions { marks, wake_group }: ZxioSocketCreationOptions<'_>,
737 ) -> Result<Result<Self, ZxioErrorCode>, zx::Status> {
738 let zxio = Zxio::default();
739 let mut out_context = zxio.as_storage_ptr() as *mut c_void;
740 let mut out_code = 0;
741
742 let ZxioWakeGroupToken(wake_group) = wake_group;
743 let creation_opts = zxio::zxio_socket_creation_options {
744 num_marks: marks.len(),
745 marks: marks.as_mut_ptr() as *mut _,
746 wake_group,
747 ..Default::default()
748 };
749
750 #[allow(
751 clippy::undocumented_unsafe_blocks,
752 reason = "Force documented unsafe blocks in Starnix"
753 )]
754 let status = unsafe {
755 zxio::zxio_socket_with_options(
756 Some(service_connector::<S>),
757 domain,
758 socket_type,
759 protocol,
760 creation_opts,
761 Some(storage_allocator),
762 &mut out_context as *mut *mut c_void,
763 &mut out_code,
764 )
765 };
766 zx::ok(status)?;
767 match out_code {
768 0 => Ok(Ok(zxio)),
769 _ => Ok(Err(ZxioErrorCode(out_code))),
770 }
771 }
772
773 fn as_ptr(&self) -> *mut zxio::zxio_t {
774 &self.inner.storage.io as *const zxio::zxio_t as *mut zxio::zxio_t
775 }
776
777 fn as_storage_ptr(&self) -> *mut zxio::zxio_storage_t {
778 &self.inner.storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t
779 }
780
781 pub fn create(handle: zx::NullableHandle) -> Result<Zxio, zx::Status> {
782 let zxio = Zxio::default();
783 #[allow(
784 clippy::undocumented_unsafe_blocks,
785 reason = "Force documented unsafe blocks in Starnix"
786 )]
787 let status = unsafe { zxio::zxio_create(handle.into_raw(), zxio.as_storage_ptr()) };
788 zx::ok(status)?;
789 Ok(zxio)
790 }
791
792 pub fn release(self) -> Result<zx::NullableHandle, zx::Status> {
793 let mut handle = 0;
794 #[allow(
795 clippy::undocumented_unsafe_blocks,
796 reason = "Force documented unsafe blocks in Starnix"
797 )]
798 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut handle) };
799 zx::ok(status)?;
800 #[allow(
801 clippy::undocumented_unsafe_blocks,
802 reason = "Force documented unsafe blocks in Starnix"
803 )]
804 unsafe {
805 Ok(zx::NullableHandle::from_raw(handle))
806 }
807 }
808
809 pub fn open(
810 &self,
811 path: &str,
812 flags: fio::Flags,
813 mut options: ZxioOpenOptions<'_, '_>,
814 ) -> Result<Self, zx::Status> {
815 let zxio = Zxio::default();
816
817 let mut zxio_open_options = zxio::zxio_open_options::default();
818 zxio_open_options.inout_attr = match &mut options.attributes {
819 Some(a) => (*a) as *mut zxio_node_attributes_t,
820 None => std::ptr::null_mut(),
821 };
822 zxio_open_options.create_attr = match &options.create_attributes {
823 Some(a) => a as *const zxio_node_attributes_t,
824 None => std::ptr::null_mut(),
825 };
826
827 #[allow(
828 clippy::undocumented_unsafe_blocks,
829 reason = "Force documented unsafe blocks in Starnix"
830 )]
831 let status = unsafe {
832 zxio::zxio_open(
833 self.as_ptr(),
834 path.as_ptr() as *const c_char,
835 path.len(),
836 flags.bits(),
837 &zxio_open_options,
838 zxio.as_storage_ptr(),
839 )
840 };
841 options.init_context_from_read();
842 if let Some(attributes) = options.attributes {
843 clean_pointer_fields(attributes);
844 }
845 zx::ok(status)?;
846 Ok(zxio)
847 }
848
849 pub fn create_with_on_representation(
850 handle: zx::NullableHandle,
851 attributes: Option<&mut zxio_node_attributes_t>,
852 ) -> Result<Zxio, zx::Status> {
853 if let Some(attr) = &attributes {
854 validate_pointer_fields(attr);
855 }
856 let zxio = Zxio::default();
857 #[allow(
858 clippy::undocumented_unsafe_blocks,
859 reason = "Force documented unsafe blocks in Starnix"
860 )]
861 let status = unsafe {
862 zxio::zxio_create_with_on_representation(
863 handle.into_raw(),
864 attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
865 zxio.as_storage_ptr(),
866 )
867 };
868 zx::ok(status)?;
869 Ok(zxio)
870 }
871
872 pub fn open_node(
874 &self,
875 path: &str,
876 flags: fio::Flags,
877 attributes: Option<&mut zxio_node_attributes_t>,
878 ) -> Result<Self, zx::Status> {
879 let zxio = Zxio::default();
880
881 #[allow(
882 clippy::undocumented_unsafe_blocks,
883 reason = "Force documented unsafe blocks in Starnix"
884 )]
885 let status = unsafe {
886 zxio::zxio_open(
887 self.as_ptr(),
888 path.as_ptr() as *const c_char,
889 path.len(),
890 (flags | fio::Flags::PROTOCOL_NODE).bits(),
893 &zxio::zxio_open_options {
894 inout_attr: attributes.map(|a| a as *mut _).unwrap_or(std::ptr::null_mut()),
895 ..Default::default()
896 },
897 zxio.as_storage_ptr(),
898 )
899 };
900 zx::ok(status)?;
901 Ok(zxio)
902 }
903
904 pub fn unlink(&self, name: &str, flags: fio::UnlinkFlags) -> Result<(), zx::Status> {
905 let flags_bits = flags.bits().try_into().map_err(|_| zx::Status::INVALID_ARGS)?;
906 #[allow(
907 clippy::undocumented_unsafe_blocks,
908 reason = "Force documented unsafe blocks in Starnix"
909 )]
910 let status = unsafe {
911 zxio::zxio_unlink(self.as_ptr(), name.as_ptr() as *const c_char, name.len(), flags_bits)
912 };
913 zx::ok(status)
914 }
915
916 pub fn read(&self, data: &mut [u8]) -> Result<usize, zx::Status> {
917 let flags = zxio::zxio_flags_t::default();
918 let mut actual = 0usize;
919 #[allow(
920 clippy::undocumented_unsafe_blocks,
921 reason = "Force documented unsafe blocks in Starnix"
922 )]
923 let status = unsafe {
924 zxio::zxio_read(
925 self.as_ptr(),
926 data.as_ptr() as *mut c_void,
927 data.len(),
928 flags,
929 &mut actual,
930 )
931 };
932 zx::ok(status)?;
933 Ok(actual)
934 }
935
936 pub unsafe fn readv(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
948 let flags = zxio::zxio_flags_t::default();
949 let mut actual = 0usize;
950 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
951 let status = unsafe {
952 zxio::zxio_readv(
953 self.as_ptr(),
954 data.as_ptr() as *const zxio::zx_iovec,
955 data.len(),
956 flags,
957 &mut actual,
958 )
959 };
960 zx::ok(status)?;
961 Ok(actual)
962 }
963
964 pub fn deep_clone(&self) -> Result<Zxio, zx::Status> {
965 Zxio::create(self.clone_handle()?)
966 }
967
968 pub fn clone_handle(&self) -> Result<zx::NullableHandle, zx::Status> {
969 let mut handle = 0;
970 #[allow(
971 clippy::undocumented_unsafe_blocks,
972 reason = "Force documented unsafe blocks in Starnix"
973 )]
974 let status = unsafe { zxio::zxio_clone(self.as_ptr(), &mut handle) };
975 zx::ok(status)?;
976 #[allow(
977 clippy::undocumented_unsafe_blocks,
978 reason = "Force documented unsafe blocks in Starnix"
979 )]
980 unsafe {
981 Ok(zx::NullableHandle::from_raw(handle))
982 }
983 }
984
985 pub fn downgrade(&self) -> ZxioWeak {
986 ZxioWeak(PinWeak::downgrade(self.inner.clone()))
987 }
988
989 pub fn read_at(&self, offset: u64, data: &mut [u8]) -> Result<usize, zx::Status> {
990 let flags = zxio::zxio_flags_t::default();
991 let mut actual = 0usize;
992 #[allow(
993 clippy::undocumented_unsafe_blocks,
994 reason = "Force documented unsafe blocks in Starnix"
995 )]
996 let status = unsafe {
997 zxio::zxio_read_at(
998 self.as_ptr(),
999 offset,
1000 data.as_ptr() as *mut c_void,
1001 data.len(),
1002 flags,
1003 &mut actual,
1004 )
1005 };
1006 zx::ok(status)?;
1007 Ok(actual)
1008 }
1009
1010 pub unsafe fn readv_at(
1023 &self,
1024 offset: u64,
1025 data: &[zxio::zx_iovec],
1026 ) -> Result<usize, zx::Status> {
1027 let flags = zxio::zxio_flags_t::default();
1028 let mut actual = 0usize;
1029 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1030 let status = unsafe {
1031 zxio::zxio_readv_at(
1032 self.as_ptr(),
1033 offset,
1034 data.as_ptr() as *const zxio::zx_iovec,
1035 data.len(),
1036 flags,
1037 &mut actual,
1038 )
1039 };
1040 zx::ok(status)?;
1041 Ok(actual)
1042 }
1043
1044 pub fn write(&self, data: &[u8]) -> Result<usize, zx::Status> {
1045 let flags = zxio::zxio_flags_t::default();
1046 let mut actual = 0;
1047 #[allow(
1048 clippy::undocumented_unsafe_blocks,
1049 reason = "Force documented unsafe blocks in Starnix"
1050 )]
1051 let status = unsafe {
1052 zxio::zxio_write(
1053 self.as_ptr(),
1054 data.as_ptr() as *const c_void,
1055 data.len(),
1056 flags,
1057 &mut actual,
1058 )
1059 };
1060 zx::ok(status)?;
1061 Ok(actual)
1062 }
1063
1064 pub unsafe fn writev(&self, data: &[zxio::zx_iovec]) -> Result<usize, zx::Status> {
1077 let flags = zxio::zxio_flags_t::default();
1078 let mut actual = 0;
1079 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1080 let status = unsafe {
1081 zxio::zxio_writev(
1082 self.as_ptr(),
1083 data.as_ptr() as *const zxio::zx_iovec,
1084 data.len(),
1085 flags,
1086 &mut actual,
1087 )
1088 };
1089 zx::ok(status)?;
1090 Ok(actual)
1091 }
1092
1093 pub fn write_at(&self, offset: u64, data: &[u8]) -> Result<usize, zx::Status> {
1094 let flags = zxio::zxio_flags_t::default();
1095 let mut actual = 0;
1096 #[allow(
1097 clippy::undocumented_unsafe_blocks,
1098 reason = "Force documented unsafe blocks in Starnix"
1099 )]
1100 let status = unsafe {
1101 zxio::zxio_write_at(
1102 self.as_ptr(),
1103 offset,
1104 data.as_ptr() as *const c_void,
1105 data.len(),
1106 flags,
1107 &mut actual,
1108 )
1109 };
1110 zx::ok(status)?;
1111 Ok(actual)
1112 }
1113
1114 pub unsafe fn writev_at(
1127 &self,
1128 offset: u64,
1129 data: &[zxio::zx_iovec],
1130 ) -> Result<usize, zx::Status> {
1131 let flags = zxio::zxio_flags_t::default();
1132 let mut actual = 0;
1133 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1134 let status = unsafe {
1135 zxio::zxio_writev_at(
1136 self.as_ptr(),
1137 offset,
1138 data.as_ptr() as *const zxio::zx_iovec,
1139 data.len(),
1140 flags,
1141 &mut actual,
1142 )
1143 };
1144 zx::ok(status)?;
1145 Ok(actual)
1146 }
1147
1148 pub fn truncate(&self, length: u64) -> Result<(), zx::Status> {
1149 #[allow(
1150 clippy::undocumented_unsafe_blocks,
1151 reason = "Force documented unsafe blocks in Starnix"
1152 )]
1153 let status = unsafe { zxio::zxio_truncate(self.as_ptr(), length) };
1154 zx::ok(status)?;
1155 Ok(())
1156 }
1157
1158 pub fn seek(&self, seek_origin: SeekOrigin, offset: i64) -> Result<usize, zx::Status> {
1159 let mut result = 0;
1160 #[allow(
1161 clippy::undocumented_unsafe_blocks,
1162 reason = "Force documented unsafe blocks in Starnix"
1163 )]
1164 let status =
1165 unsafe { zxio::zxio_seek(self.as_ptr(), seek_origin.into(), offset, &mut result) };
1166 zx::ok(status)?;
1167 Ok(result)
1168 }
1169
1170 pub fn vmo_get(&self, flags: zx::VmarFlags) -> Result<zx::Vmo, zx::Status> {
1171 let mut vmo = 0;
1172 #[allow(
1173 clippy::undocumented_unsafe_blocks,
1174 reason = "Force documented unsafe blocks in Starnix"
1175 )]
1176 let status = unsafe { zxio::zxio_vmo_get(self.as_ptr(), flags.bits(), &mut vmo) };
1177 zx::ok(status)?;
1178 #[allow(
1179 clippy::undocumented_unsafe_blocks,
1180 reason = "Force documented unsafe blocks in Starnix"
1181 )]
1182 let handle = unsafe { zx::NullableHandle::from_raw(vmo) };
1183 Ok(zx::Vmo::from(handle))
1184 }
1185
1186 fn node_attributes_from_query(
1187 &self,
1188 query: zxio_node_attr_has_t,
1189 fsverity_root_hash: Option<&mut [u8; ZXIO_ROOT_HASH_LENGTH]>,
1190 ) -> zxio_node_attributes_t {
1191 if let Some(fsverity_root_hash) = fsverity_root_hash {
1192 zxio_node_attributes_t {
1193 has: query,
1194 fsverity_root_hash: fsverity_root_hash as *mut u8,
1195 ..Default::default()
1196 }
1197 } else {
1198 zxio_node_attributes_t { has: query, ..Default::default() }
1199 }
1200 }
1201
1202 pub fn attr_get(
1203 &self,
1204 query: zxio_node_attr_has_t,
1205 ) -> Result<zxio_node_attributes_t, zx::Status> {
1206 let mut attributes = self.node_attributes_from_query(query, None);
1207 #[allow(
1208 clippy::undocumented_unsafe_blocks,
1209 reason = "Force documented unsafe blocks in Starnix"
1210 )]
1211 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1212 zx::ok(status)?;
1213 Ok(attributes)
1214 }
1215
1216 pub fn close_and_update_access_time(self) -> Result<(), zx::Status> {
1222 let mut out_handle = zx::sys::ZX_HANDLE_INVALID;
1223 let status = unsafe { zxio::zxio_release(self.as_ptr(), &mut out_handle) };
1225 zx::ok(status)?;
1226 let proxy = fio::NodeSynchronousProxy::from_channel(
1227 unsafe { zx::NullableHandle::from_raw(out_handle) }.into(),
1229 );
1230
1231 let _ = proxy.get_attributes(
1236 fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
1237 zx::MonotonicInstant::INFINITE_PAST,
1238 );
1239
1240 Ok(())
1243 }
1244
1245 pub fn attr_get_with_root_hash(
1247 &self,
1248 query: zxio_node_attr_has_t,
1249 fsverity_root_hash: &mut [u8; ZXIO_ROOT_HASH_LENGTH],
1250 ) -> Result<zxio_node_attributes_t, zx::Status> {
1251 let mut attributes = self.node_attributes_from_query(query, Some(fsverity_root_hash));
1252 #[allow(
1253 clippy::undocumented_unsafe_blocks,
1254 reason = "Force documented unsafe blocks in Starnix"
1255 )]
1256 let status = unsafe { zxio::zxio_attr_get(self.as_ptr(), &mut attributes) };
1257 clean_pointer_fields(&mut attributes);
1258 zx::ok(status)?;
1259 Ok(attributes)
1260 }
1261
1262 pub fn attr_set(&self, attributes: &zxio_node_attributes_t) -> Result<(), zx::Status> {
1263 validate_pointer_fields(attributes);
1264 #[allow(
1265 clippy::undocumented_unsafe_blocks,
1266 reason = "Force documented unsafe blocks in Starnix"
1267 )]
1268 let status = unsafe { zxio::zxio_attr_set(self.as_ptr(), attributes) };
1269 zx::ok(status)?;
1270 Ok(())
1271 }
1272
1273 pub fn enable_verity(&self, descriptor: &zxio_fsverity_descriptor_t) -> Result<(), zx::Status> {
1274 #[allow(
1275 clippy::undocumented_unsafe_blocks,
1276 reason = "Force documented unsafe blocks in Starnix"
1277 )]
1278 let status = unsafe { zxio::zxio_enable_verity(self.as_ptr(), descriptor) };
1279 zx::ok(status)?;
1280 Ok(())
1281 }
1282
1283 pub fn rename(
1284 &self,
1285 old_path: &str,
1286 new_directory: &Zxio,
1287 new_path: &str,
1288 ) -> Result<(), zx::Status> {
1289 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1290 #[allow(
1291 clippy::undocumented_unsafe_blocks,
1292 reason = "Force documented unsafe blocks in Starnix"
1293 )]
1294 let status = unsafe { zxio::zxio_token_get(new_directory.as_ptr(), &mut handle) };
1295 zx::ok(status)?;
1296 #[allow(
1297 clippy::undocumented_unsafe_blocks,
1298 reason = "Force documented unsafe blocks in Starnix"
1299 )]
1300 let status = unsafe {
1301 zxio::zxio_rename(
1302 self.as_ptr(),
1303 old_path.as_ptr() as *const c_char,
1304 old_path.len(),
1305 handle,
1306 new_path.as_ptr() as *const c_char,
1307 new_path.len(),
1308 )
1309 };
1310 zx::ok(status)?;
1311 Ok(())
1312 }
1313
1314 pub fn wait_begin(
1315 &self,
1316 zxio_signals: zxio_signals_t,
1317 ) -> (zx::Unowned<'_, zx::NullableHandle>, zx::Signals) {
1318 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1319 let mut zx_signals = zx::sys::ZX_SIGNAL_NONE;
1320 #[allow(
1321 clippy::undocumented_unsafe_blocks,
1322 reason = "Force documented unsafe blocks in Starnix"
1323 )]
1324 unsafe {
1325 zxio::zxio_wait_begin(self.as_ptr(), zxio_signals, &mut handle, &mut zx_signals)
1326 };
1327 #[allow(
1328 clippy::undocumented_unsafe_blocks,
1329 reason = "Force documented unsafe blocks in Starnix"
1330 )]
1331 let handle = unsafe { zx::Unowned::<zx::NullableHandle>::from_raw_handle(handle) };
1332 let signals = zx::Signals::from_bits_truncate(zx_signals);
1333 (handle, signals)
1334 }
1335
1336 pub fn wait_end(&self, signals: zx::Signals) -> zxio_signals_t {
1337 let mut zxio_signals = ZxioSignals::NONE.bits();
1338 #[allow(
1339 clippy::undocumented_unsafe_blocks,
1340 reason = "Force documented unsafe blocks in Starnix"
1341 )]
1342 unsafe {
1343 zxio::zxio_wait_end(self.as_ptr(), signals.bits(), &mut zxio_signals);
1344 }
1345 zxio_signals
1346 }
1347
1348 pub fn create_dirent_iterator(&self) -> Result<DirentIterator<'_>, zx::Status> {
1349 let mut zxio_iterator = Box::default();
1350 #[allow(
1351 clippy::undocumented_unsafe_blocks,
1352 reason = "Force documented unsafe blocks in Starnix"
1353 )]
1354 let status = unsafe { zxio::zxio_dirent_iterator_init(&mut *zxio_iterator, self.as_ptr()) };
1355 zx::ok(status)?;
1356 let iterator =
1357 DirentIterator { iterator: zxio_iterator, _directory: PhantomData, finished: false };
1358 Ok(iterator)
1359 }
1360
1361 pub fn connect(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1362 let mut out_code = 0;
1363 #[allow(
1364 clippy::undocumented_unsafe_blocks,
1365 reason = "Force documented unsafe blocks in Starnix"
1366 )]
1367 let status = unsafe {
1368 zxio::zxio_connect(
1369 self.as_ptr(),
1370 addr.as_ptr() as *const sockaddr,
1371 addr.len() as socklen_t,
1372 &mut out_code,
1373 )
1374 };
1375 zx::ok(status)?;
1376 match out_code {
1377 0 => Ok(Ok(())),
1378 _ => Ok(Err(ZxioErrorCode(out_code))),
1379 }
1380 }
1381
1382 pub fn bind(&self, addr: &[u8]) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1383 let mut out_code = 0;
1384 #[allow(
1385 clippy::undocumented_unsafe_blocks,
1386 reason = "Force documented unsafe blocks in Starnix"
1387 )]
1388 let status = unsafe {
1389 zxio::zxio_bind(
1390 self.as_ptr(),
1391 addr.as_ptr() as *const sockaddr,
1392 addr.len() as socklen_t,
1393 &mut out_code,
1394 )
1395 };
1396 zx::ok(status)?;
1397 match out_code {
1398 0 => Ok(Ok(())),
1399 _ => Ok(Err(ZxioErrorCode(out_code))),
1400 }
1401 }
1402
1403 pub fn listen(&self, backlog: i32) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1404 let mut out_code = 0;
1405 #[allow(
1406 clippy::undocumented_unsafe_blocks,
1407 reason = "Force documented unsafe blocks in Starnix"
1408 )]
1409 let status = unsafe { zxio::zxio_listen(self.as_ptr(), backlog as c_int, &mut out_code) };
1410 zx::ok(status)?;
1411 match out_code {
1412 0 => Ok(Ok(())),
1413 _ => Ok(Err(ZxioErrorCode(out_code))),
1414 }
1415 }
1416
1417 pub fn accept(&self) -> Result<Result<Zxio, ZxioErrorCode>, zx::Status> {
1418 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1419 let mut addr = vec![0u8; addrlen as usize];
1420 let zxio = Zxio::default();
1421 let mut out_code = 0;
1422 #[allow(
1423 clippy::undocumented_unsafe_blocks,
1424 reason = "Force documented unsafe blocks in Starnix"
1425 )]
1426 let status = unsafe {
1427 zxio::zxio_accept(
1428 self.as_ptr(),
1429 addr.as_mut_ptr() as *mut sockaddr,
1430 &mut addrlen,
1431 zxio.as_storage_ptr(),
1432 &mut out_code,
1433 )
1434 };
1435 zx::ok(status)?;
1436 match out_code {
1437 0 => Ok(Ok(zxio)),
1438 _ => Ok(Err(ZxioErrorCode(out_code))),
1439 }
1440 }
1441
1442 pub fn getsockname(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1443 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1444 let mut addr = vec![0u8; addrlen as usize];
1445 let mut out_code = 0;
1446 #[allow(
1447 clippy::undocumented_unsafe_blocks,
1448 reason = "Force documented unsafe blocks in Starnix"
1449 )]
1450 let status = unsafe {
1451 zxio::zxio_getsockname(
1452 self.as_ptr(),
1453 addr.as_mut_ptr() as *mut sockaddr,
1454 &mut addrlen,
1455 &mut out_code,
1456 )
1457 };
1458 zx::ok(status)?;
1459 match out_code {
1460 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1461 _ => Ok(Err(ZxioErrorCode(out_code))),
1462 }
1463 }
1464
1465 pub fn getpeername(&self) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1466 let mut addrlen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
1467 let mut addr = vec![0u8; addrlen as usize];
1468 let mut out_code = 0;
1469 #[allow(
1470 clippy::undocumented_unsafe_blocks,
1471 reason = "Force documented unsafe blocks in Starnix"
1472 )]
1473 let status = unsafe {
1474 zxio::zxio_getpeername(
1475 self.as_ptr(),
1476 addr.as_mut_ptr() as *mut sockaddr,
1477 &mut addrlen,
1478 &mut out_code,
1479 )
1480 };
1481 zx::ok(status)?;
1482 match out_code {
1483 0 => Ok(Ok(addr[..addrlen as usize].to_vec())),
1484 _ => Ok(Err(ZxioErrorCode(out_code))),
1485 }
1486 }
1487
1488 pub fn getsockopt_slice(
1489 &self,
1490 level: u32,
1491 optname: u32,
1492 optval: &mut [u8],
1493 ) -> Result<Result<socklen_t, ZxioErrorCode>, zx::Status> {
1494 let mut optlen = optval.len() as socklen_t;
1495 let mut out_code = 0;
1496 #[allow(
1497 clippy::undocumented_unsafe_blocks,
1498 reason = "Force documented unsafe blocks in Starnix"
1499 )]
1500 let status = unsafe {
1501 zxio::zxio_getsockopt(
1502 self.as_ptr(),
1503 level as c_int,
1504 optname as c_int,
1505 optval.as_mut_ptr() as *mut c_void,
1506 &mut optlen,
1507 &mut out_code,
1508 )
1509 };
1510 zx::ok(status)?;
1511 match out_code {
1512 0 => Ok(Ok(optlen)),
1513 _ => Ok(Err(ZxioErrorCode(out_code))),
1514 }
1515 }
1516
1517 pub fn getsockopt(
1518 &self,
1519 level: u32,
1520 optname: u32,
1521 optlen: socklen_t,
1522 ) -> Result<Result<Vec<u8>, ZxioErrorCode>, zx::Status> {
1523 let mut optval = vec![0u8; optlen as usize];
1524 let result = self.getsockopt_slice(level, optname, &mut optval[..])?;
1525 Ok(result.map(|optlen| optval[..optlen as usize].to_vec()))
1526 }
1527
1528 pub fn setsockopt(
1529 &self,
1530 level: i32,
1531 optname: i32,
1532 optval: &[u8],
1533 access_token: Option<zx::NullableHandle>,
1534 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1535 let mut out_code = 0;
1536 let access_token_handle =
1537 access_token.map(zx::NullableHandle::into_raw).unwrap_or(zx::sys::ZX_HANDLE_INVALID);
1538
1539 #[allow(
1540 clippy::undocumented_unsafe_blocks,
1541 reason = "Force documented unsafe blocks in Starnix"
1542 )]
1543 let status = unsafe {
1544 zxio::zxio_setsockopt(
1545 self.as_ptr(),
1546 level,
1547 optname,
1548 optval.as_ptr() as *const c_void,
1549 optval.len() as socklen_t,
1550 access_token_handle,
1551 &mut out_code,
1552 )
1553 };
1554 zx::ok(status)?;
1555 match out_code {
1556 0 => Ok(Ok(())),
1557 _ => Ok(Err(ZxioErrorCode(out_code))),
1558 }
1559 }
1560
1561 pub fn shutdown(
1562 &self,
1563 flags: ZxioShutdownFlags,
1564 ) -> Result<Result<(), ZxioErrorCode>, zx::Status> {
1565 let mut out_code = 0;
1566 #[allow(
1567 clippy::undocumented_unsafe_blocks,
1568 reason = "Force documented unsafe blocks in Starnix"
1569 )]
1570 let status = unsafe { zxio::zxio_shutdown(self.as_ptr(), flags.bits(), &mut out_code) };
1571 zx::ok(status)?;
1572 match out_code {
1573 0 => Ok(Ok(())),
1574 _ => Ok(Err(ZxioErrorCode(out_code))),
1575 }
1576 }
1577
1578 pub fn sendmsg(
1579 &self,
1580 addr: &mut [u8],
1581 buffer: &mut [zxio::iovec],
1582 cmsg: &[ControlMessage],
1583 flags: u32,
1584 ) -> Result<Result<usize, ZxioErrorCode>, zx::Status> {
1585 let mut msg = zxio::msghdr::default();
1586 msg.msg_name = match addr.len() {
1587 0 => std::ptr::null_mut() as *mut c_void,
1588 _ => addr.as_mut_ptr() as *mut c_void,
1589 };
1590 msg.msg_namelen = addr.len() as u32;
1591
1592 msg.msg_iovlen =
1593 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1594 msg.msg_iov = buffer.as_mut_ptr();
1595
1596 let mut cmsg_buffer = serialize_control_messages(cmsg);
1597 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1598 msg.msg_controllen = cmsg_buffer.len() as u32;
1599
1600 let mut out_code = 0;
1601 let mut out_actual = 0;
1602
1603 #[allow(
1604 clippy::undocumented_unsafe_blocks,
1605 reason = "Force documented unsafe blocks in Starnix"
1606 )]
1607 let status = unsafe {
1608 zxio::zxio_sendmsg(self.as_ptr(), &msg, flags as c_int, &mut out_actual, &mut out_code)
1609 };
1610
1611 zx::ok(status)?;
1612 match out_code {
1613 0 => Ok(Ok(out_actual)),
1614 _ => Ok(Err(ZxioErrorCode(out_code))),
1615 }
1616 }
1617
1618 pub fn recvmsg(
1619 &self,
1620 buffer: &mut [zxio::iovec],
1621 flags: u32,
1622 ) -> Result<Result<RecvMessageInfo, ZxioErrorCode>, zx::Status> {
1623 let mut msg = msghdr::default();
1624 let mut addr = vec![0u8; std::mem::size_of::<sockaddr_storage>()];
1625 msg.msg_name = addr.as_mut_ptr() as *mut c_void;
1626 msg.msg_namelen = addr.len() as u32;
1627
1628 let max_buffer_capacity = buffer.iter().map(|v| v.iov_len).sum();
1629 msg.msg_iovlen =
1630 i32::try_from(buffer.len()).map_err(|_: TryFromIntError| zx::Status::INVALID_ARGS)?;
1631 msg.msg_iov = buffer.as_mut_ptr();
1632
1633 let mut cmsg_buffer = vec![0u8; MAX_CMSGS_BUFFER];
1634 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
1635 msg.msg_controllen = cmsg_buffer.len() as u32;
1636
1637 let mut out_code = 0;
1638 let mut out_actual = 0;
1639 #[allow(
1640 clippy::undocumented_unsafe_blocks,
1641 reason = "Force documented unsafe blocks in Starnix"
1642 )]
1643 let status = unsafe {
1644 zxio::zxio_recvmsg(
1645 self.as_ptr(),
1646 &mut msg,
1647 flags as c_int,
1648 &mut out_actual,
1649 &mut out_code,
1650 )
1651 };
1652 zx::ok(status)?;
1653
1654 if out_code != 0 {
1655 return Ok(Err(ZxioErrorCode(out_code)));
1656 }
1657
1658 let control_messages = parse_control_messages(&cmsg_buffer[..msg.msg_controllen as usize]);
1659 Ok(Ok(RecvMessageInfo {
1660 address: addr[..msg.msg_namelen as usize].to_vec(),
1661 bytes_read: std::cmp::min(max_buffer_capacity, out_actual),
1662 message_length: out_actual,
1663 control_messages,
1664 flags: msg.msg_flags,
1665 }))
1666 }
1667
1668 pub fn read_link(&self) -> Result<&[u8], zx::Status> {
1669 let mut target = std::ptr::null();
1670 let mut target_len = 0;
1671 #[allow(
1672 clippy::undocumented_unsafe_blocks,
1673 reason = "Force documented unsafe blocks in Starnix"
1674 )]
1675 let status = unsafe { zxio::zxio_read_link(self.as_ptr(), &mut target, &mut target_len) };
1676 zx::ok(status)?;
1677 unsafe { Ok(std::slice::from_raw_parts(target, target_len)) }
1679 }
1680
1681 pub fn create_symlink(&self, name: &str, target: &[u8]) -> Result<Zxio, zx::Status> {
1682 let name = name.as_bytes();
1683 let zxio = Zxio::default();
1684 #[allow(
1685 clippy::undocumented_unsafe_blocks,
1686 reason = "Force documented unsafe blocks in Starnix"
1687 )]
1688 let status = unsafe {
1689 zxio::zxio_create_symlink(
1690 self.as_ptr(),
1691 name.as_ptr() as *const c_char,
1692 name.len(),
1693 target.as_ptr(),
1694 target.len(),
1695 zxio.as_storage_ptr(),
1696 )
1697 };
1698 zx::ok(status)?;
1699 Ok(zxio)
1700 }
1701
1702 pub fn xattr_list(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
1703 unsafe extern "C" fn callback(context: *mut c_void, name: *const u8, name_len: usize) {
1704 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1705 let out_names = unsafe { &mut *(context as *mut Vec<Vec<u8>>) };
1706 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1707 let name_slice = unsafe { std::slice::from_raw_parts(name, name_len) };
1708 out_names.push(name_slice.to_vec());
1709 }
1710 let mut out_names = Vec::new();
1711 #[allow(
1712 clippy::undocumented_unsafe_blocks,
1713 reason = "Force documented unsafe blocks in Starnix"
1714 )]
1715 let status = unsafe {
1716 zxio::zxio_xattr_list(
1717 self.as_ptr(),
1718 Some(callback),
1719 &mut out_names as *mut _ as *mut c_void,
1720 )
1721 };
1722 zx::ok(status)?;
1723 Ok(out_names)
1724 }
1725
1726 pub fn xattr_get(&self, name: &[u8]) -> Result<Vec<u8>, zx::Status> {
1727 unsafe extern "C" fn callback(
1728 context: *mut c_void,
1729 data: zxio::zxio_xattr_data_t,
1730 ) -> zx_status_t {
1731 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1732 let out_value = unsafe { &mut *(context as *mut Vec<u8>) };
1733 if data.data.is_null() {
1734 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1735 let value_vmo = unsafe { zx::Unowned::<'_, zx::Vmo>::from_raw_handle(data.vmo) };
1736 match value_vmo.read_to_vec(0, data.len as u64) {
1737 Ok(vec) => *out_value = vec,
1738 Err(status) => return status.into_raw(),
1739 }
1740 } else {
1741 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1742 let value_slice =
1743 unsafe { std::slice::from_raw_parts(data.data as *mut u8, data.len) };
1744 out_value.extend_from_slice(value_slice);
1745 }
1746 zx::sys::ZX_OK
1747 }
1748 let mut out_value = Vec::new();
1749 #[allow(
1750 clippy::undocumented_unsafe_blocks,
1751 reason = "Force documented unsafe blocks in Starnix"
1752 )]
1753 let status = unsafe {
1754 zxio::zxio_xattr_get(
1755 self.as_ptr(),
1756 name.as_ptr(),
1757 name.len(),
1758 Some(callback),
1759 &mut out_value as *mut _ as *mut c_void,
1760 )
1761 };
1762 zx::ok(status)?;
1763 Ok(out_value)
1764 }
1765
1766 pub fn xattr_set(
1767 &self,
1768 name: &[u8],
1769 value: &[u8],
1770 mode: XattrSetMode,
1771 ) -> Result<(), zx::Status> {
1772 #[allow(
1773 clippy::undocumented_unsafe_blocks,
1774 reason = "Force documented unsafe blocks in Starnix"
1775 )]
1776 let status = unsafe {
1777 zxio::zxio_xattr_set(
1778 self.as_ptr(),
1779 name.as_ptr(),
1780 name.len(),
1781 value.as_ptr(),
1782 value.len(),
1783 mode as u32,
1784 )
1785 };
1786 zx::ok(status)
1787 }
1788
1789 pub fn xattr_remove(&self, name: &[u8]) -> Result<(), zx::Status> {
1790 #[allow(
1791 clippy::undocumented_unsafe_blocks,
1792 reason = "Force documented unsafe blocks in Starnix"
1793 )]
1794 zx::ok(unsafe { zxio::zxio_xattr_remove(self.as_ptr(), name.as_ptr(), name.len()) })
1795 }
1796
1797 pub fn link_into(&self, target_dir: &Zxio, name: &str) -> Result<(), zx::Status> {
1798 let mut handle = zx::sys::ZX_HANDLE_INVALID;
1799 #[allow(
1800 clippy::undocumented_unsafe_blocks,
1801 reason = "Force documented unsafe blocks in Starnix"
1802 )]
1803 zx::ok(unsafe { zxio::zxio_token_get(target_dir.as_ptr(), &mut handle) })?;
1804 #[allow(
1805 clippy::undocumented_unsafe_blocks,
1806 reason = "Force documented unsafe blocks in Starnix"
1807 )]
1808 zx::ok(unsafe {
1809 zxio::zxio_link_into(self.as_ptr(), handle, name.as_ptr() as *const c_char, name.len())
1810 })
1811 }
1812
1813 pub fn allocate(&self, offset: u64, len: u64, mode: AllocateMode) -> Result<(), zx::Status> {
1814 #[allow(
1815 clippy::undocumented_unsafe_blocks,
1816 reason = "Force documented unsafe blocks in Starnix"
1817 )]
1818 let status = unsafe { zxio::zxio_allocate(self.as_ptr(), offset, len, mode.bits()) };
1819 zx::ok(status)
1820 }
1821
1822 pub fn sync(&self) -> Result<(), zx::Status> {
1823 #[allow(
1824 clippy::undocumented_unsafe_blocks,
1825 reason = "Force documented unsafe blocks in Starnix"
1826 )]
1827 let status = unsafe { zxio::zxio_sync(self.as_ptr()) };
1828 zx::ok(status)
1829 }
1830
1831 pub fn close(&self) -> Result<(), zx::Status> {
1832 #[allow(
1833 clippy::undocumented_unsafe_blocks,
1834 reason = "Force documented unsafe blocks in Starnix"
1835 )]
1836 let status = unsafe { zxio::zxio_close(self.as_ptr()) };
1837 zx::ok(status)
1838 }
1839
1840 pub fn get_read_buffer_available(&self) -> Result<usize, zx::Status> {
1841 let mut available = 0usize;
1842 let status = unsafe { zxio::zxio_get_read_buffer_available(self.as_ptr(), &mut available) };
1845 zx::ok(status)?;
1846 Ok(available)
1847 }
1848}
1849
1850impl Drop for ZxioStorage {
1851 fn drop(&mut self) {
1852 let zxio_ptr: *mut zxio::zxio_t = &mut self.storage.io;
1857 #[allow(
1858 clippy::undocumented_unsafe_blocks,
1859 reason = "Force documented unsafe blocks in Starnix"
1860 )]
1861 unsafe {
1862 zxio::zxio_destroy(zxio_ptr);
1863 };
1864 }
1865}
1866
1867impl Clone for Zxio {
1868 fn clone(&self) -> Self {
1869 Self { inner: self.inner.clone() }
1870 }
1871}
1872
1873enum NodeKind {
1874 File,
1875 Directory,
1876 Symlink,
1877 Unknown,
1878}
1879
1880impl From<fio::Representation> for NodeKind {
1881 fn from(representation: fio::Representation) -> Self {
1882 match representation {
1883 fio::Representation::File(_) => NodeKind::File,
1884 fio::Representation::Directory(_) => NodeKind::Directory,
1885 fio::Representation::Symlink(_) => NodeKind::Symlink,
1886 _ => NodeKind::Unknown,
1887 }
1888 }
1889}
1890
1891struct DescribedNode {
1896 node: fio::NodeSynchronousProxy,
1897 kind: NodeKind,
1898}
1899
1900fn directory_open(
1910 directory: &fio::DirectorySynchronousProxy,
1911 path: &str,
1912 flags: fio::Flags,
1913 deadline: zx::MonotonicInstant,
1914) -> Result<DescribedNode, zx::Status> {
1915 let flags = flags | fio::Flags::FLAG_SEND_REPRESENTATION;
1916
1917 let (client_end, server_end) = zx::Channel::create();
1918 directory.open(path, flags, &Default::default(), server_end).map_err(|_| zx::Status::IO)?;
1919 let node = fio::NodeSynchronousProxy::new(client_end);
1920
1921 match node.wait_for_event(deadline).map_err(map_fidl_error)? {
1922 fio::NodeEvent::OnOpen_ { .. } => {
1923 panic!("Should never happen when sending FLAG_SEND_REPRESENTATION")
1924 }
1925 fio::NodeEvent::OnRepresentation { payload } => {
1926 Ok(DescribedNode { node, kind: payload.into() })
1927 }
1928 fio::NodeEvent::_UnknownEvent { .. } => Err(zx::Status::NOT_SUPPORTED),
1929 }
1930}
1931
1932pub fn directory_open_vmo(
1940 directory: &fio::DirectorySynchronousProxy,
1941 path: &str,
1942 vmo_flags: fio::VmoFlags,
1943 deadline: zx::MonotonicInstant,
1944) -> Result<zx::Vmo, zx::Status> {
1945 let mut flags = fio::Flags::empty();
1946 if vmo_flags.contains(fio::VmoFlags::READ) {
1947 flags |= fio::PERM_READABLE;
1948 }
1949 if vmo_flags.contains(fio::VmoFlags::WRITE) {
1950 flags |= fio::PERM_WRITABLE;
1951 }
1952 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
1953 flags |= fio::PERM_EXECUTABLE;
1954 }
1955 let description = directory_open(directory, path, flags, deadline)?;
1956 let file = match description.kind {
1957 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
1958 _ => return Err(zx::Status::IO),
1959 };
1960
1961 let vmo = file
1962 .get_backing_memory(vmo_flags, deadline)
1963 .map_err(map_fidl_error)?
1964 .map_err(zx::Status::err_from_raw)?;
1965 Ok(vmo)
1966}
1967
1968pub fn directory_read_file(
1973 directory: &fio::DirectorySynchronousProxy,
1974 path: &str,
1975 deadline: zx::MonotonicInstant,
1976) -> Result<Vec<u8>, zx::Status> {
1977 let description = directory_open(directory, path, fio::PERM_READABLE, deadline)?;
1978 let file = match description.kind {
1979 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
1980 _ => return Err(zx::Status::IO),
1981 };
1982
1983 let mut result = Vec::new();
1984 loop {
1985 let mut data = file
1986 .read(fio::MAX_TRANSFER_SIZE, deadline)
1987 .map_err(map_fidl_error)?
1988 .map_err(zx::Status::err_from_raw)?;
1989 let finished = (data.len() as u64) < fio::MAX_TRANSFER_SIZE;
1990 result.append(&mut data);
1991 if finished {
1992 return Ok(result);
1993 }
1994 }
1995}
1996
1997pub fn directory_create_tmp_file(
1999 directory: &fio::DirectorySynchronousProxy,
2000 flags: fio::Flags,
2001 deadline: zx::MonotonicInstant,
2002) -> Result<fio::FileSynchronousProxy, zx::Status> {
2003 let description = directory_open(
2004 directory,
2005 ".",
2006 flags
2007 | fio::PERM_WRITABLE
2008 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2009 | fio::Flags::PROTOCOL_FILE,
2010 deadline,
2011 )?;
2012 let file = match description.kind {
2013 NodeKind::File => fio::FileSynchronousProxy::new(description.node.into_channel()),
2014 _ => return Err(zx::Status::NOT_FILE),
2015 };
2016
2017 Ok(file)
2018}
2019
2020pub fn directory_open_async(
2030 directory: &fio::DirectorySynchronousProxy,
2031 path: &str,
2032 flags: fio::Flags,
2033) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2034 if flags.intersects(fio::Flags::FLAG_SEND_REPRESENTATION) {
2035 return Err(zx::Status::INVALID_ARGS);
2036 }
2037
2038 let (proxy, server_end) = fidl::endpoints::create_sync_proxy::<fio::DirectoryMarker>();
2039 directory
2040 .open(path, flags, &Default::default(), server_end.into_channel())
2041 .map_err(|_| zx::Status::IO)?;
2042 Ok(proxy)
2043}
2044
2045pub fn directory_open_directory_async(
2056 directory: &fio::DirectorySynchronousProxy,
2057 path: &str,
2058 flags: fio::Flags,
2059) -> Result<fio::DirectorySynchronousProxy, zx::Status> {
2060 let flags = flags | fio::Flags::PROTOCOL_DIRECTORY;
2061 let proxy = directory_open_async(directory, path, flags)?;
2062 Ok(proxy)
2063}
2064
2065fn map_fidl_error(error: fidl::Error) -> zx::Status {
2066 match error {
2067 fidl::Error::ClientChannelClosed { epitaph, .. } => match epitaph.into() {
2068 Err(s) => s,
2069 Ok(()) => zx::Status::PEER_CLOSED,
2070 },
2071 _ => zx::Status::IO,
2072 }
2073}
2074
2075#[cfg(test)]
2076mod test {
2077 use super::*;
2078
2079 use anyhow::Error;
2080 use fidl::endpoints::Proxy as _;
2081 use fidl_fuchsia_io as fio;
2082 use fuchsia_fs::directory;
2083
2084 fn open_pkg() -> fio::DirectorySynchronousProxy {
2085 let pkg_proxy =
2086 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2087 .expect("failed to open /pkg");
2088 fio::DirectorySynchronousProxy::new(
2089 pkg_proxy
2090 .into_channel()
2091 .expect("failed to convert proxy into channel")
2092 .into_zx_channel(),
2093 )
2094 }
2095
2096 #[fuchsia::test]
2097 async fn test_directory_open() -> Result<(), Error> {
2098 let pkg = open_pkg();
2099 let description = directory_open(
2100 &pkg,
2101 "bin/syncio_lib_test",
2102 fio::PERM_READABLE,
2103 zx::MonotonicInstant::INFINITE,
2104 )?;
2105 assert!(match description.kind {
2106 NodeKind::File => true,
2107 _ => false,
2108 });
2109 Ok(())
2110 }
2111
2112 #[fuchsia::test]
2113 async fn test_directory_open_vmo() -> Result<(), Error> {
2114 let pkg = open_pkg();
2115 let vmo = directory_open_vmo(
2116 &pkg,
2117 "bin/syncio_lib_test",
2118 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2119 zx::MonotonicInstant::INFINITE,
2120 )?;
2121 assert!(!vmo.is_invalid());
2122
2123 let info = vmo.basic_info()?;
2124 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2125 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2126 Ok(())
2127 }
2128
2129 #[fuchsia::test]
2130 async fn test_directory_read_file() -> Result<(), Error> {
2131 let pkg = open_pkg();
2132 let data =
2133 directory_read_file(&pkg, "bin/syncio_lib_test", zx::MonotonicInstant::INFINITE)?;
2134
2135 assert!(!data.is_empty());
2136 Ok(())
2137 }
2138
2139 #[fuchsia::test]
2140 async fn test_directory_open_directory_async() -> Result<(), Error> {
2141 let pkg = open_pkg();
2142 let bin =
2143 directory_open_directory_async(&pkg, "bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE)?;
2144 let vmo = directory_open_vmo(
2145 &bin,
2146 "syncio_lib_test",
2147 fio::VmoFlags::READ | fio::VmoFlags::EXECUTE,
2148 zx::MonotonicInstant::INFINITE,
2149 )?;
2150 assert!(!vmo.is_invalid());
2151
2152 let info = vmo.basic_info()?;
2153 assert_eq!(zx::Rights::READ, info.rights & zx::Rights::READ);
2154 assert_eq!(zx::Rights::EXECUTE, info.rights & zx::Rights::EXECUTE);
2155 Ok(())
2156 }
2157
2158 #[fuchsia::test]
2159 async fn test_directory_open_zxio_async() -> Result<(), Error> {
2160 let pkg_proxy =
2161 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2162 .expect("failed to open /pkg");
2163 let zx_channel = pkg_proxy
2164 .into_channel()
2165 .expect("failed to convert proxy into channel")
2166 .into_zx_channel();
2167 let storage = zxio::zxio_storage_t::default();
2168 #[allow(
2169 clippy::undocumented_unsafe_blocks,
2170 reason = "Force documented unsafe blocks in Starnix"
2171 )]
2172 let status = unsafe {
2173 zxio::zxio_create(
2174 zx_channel.into_raw(),
2175 &storage as *const zxio::zxio_storage_t as *mut zxio::zxio_storage_t,
2176 )
2177 };
2178 assert_eq!(status, zx::sys::ZX_OK);
2179 let io = &storage.io as *const zxio::zxio_t as *mut zxio::zxio_t;
2180 #[allow(
2181 clippy::undocumented_unsafe_blocks,
2182 reason = "Force documented unsafe blocks in Starnix"
2183 )]
2184 let close_status = unsafe { zxio::zxio_close(io) };
2185 assert_eq!(close_status, zx::sys::ZX_OK);
2186 #[allow(
2187 clippy::undocumented_unsafe_blocks,
2188 reason = "Force documented unsafe blocks in Starnix"
2189 )]
2190 unsafe {
2191 zxio::zxio_destroy(io);
2192 }
2193 Ok(())
2194 }
2195
2196 #[fuchsia::test]
2197 async fn test_directory_enumerate() -> Result<(), Error> {
2198 let pkg_dir_handle =
2199 directory::open_in_namespace("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE)
2200 .expect("failed to open /pkg")
2201 .into_channel()
2202 .expect("could not unwrap channel")
2203 .into_zx_channel()
2204 .into();
2205
2206 let io: Zxio = Zxio::create(pkg_dir_handle)?;
2207 let iter = io.create_dirent_iterator().expect("failed to create iterator");
2208 let expected_dir_names = vec![".", "bin", "lib", "meta"];
2209 let mut found_dir_names = iter
2210 .map(|e| {
2211 let dirent = e.expect("dirent");
2212 assert!(dirent.is_dir());
2213 std::str::from_utf8(&dirent.name).expect("name was not valid utf8").to_string()
2214 })
2215 .collect::<Vec<_>>();
2216 found_dir_names.sort();
2217 assert_eq!(expected_dir_names, found_dir_names);
2218
2219 let bin_io = io
2221 .open("bin", fio::PERM_READABLE | fio::PERM_EXECUTABLE, Default::default())
2222 .expect("open");
2223 for entry in bin_io.create_dirent_iterator().expect("failed to create iterator") {
2224 let dirent = entry.expect("dirent");
2225 if dirent.name == "." {
2226 assert!(dirent.is_dir());
2227 } else {
2228 assert!(dirent.is_file());
2229 }
2230 }
2231
2232 Ok(())
2233 }
2234
2235 #[fuchsia::test]
2236 fn test_storage_allocator() {
2237 let mut out_storage = zxio_storage_t::default();
2238 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2239
2240 let mut out_context = Zxio::default();
2241 let mut out_context_ptr = &mut out_context as *mut Zxio;
2242
2243 #[allow(
2244 clippy::undocumented_unsafe_blocks,
2245 reason = "Force documented unsafe blocks in Starnix"
2246 )]
2247 let out = unsafe {
2248 storage_allocator(
2249 0 as zxio_object_type_t,
2250 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2251 &mut out_context_ptr as *mut *mut Zxio as *mut *mut c_void,
2252 )
2253 };
2254 assert_eq!(out, zx::sys::ZX_OK);
2255 }
2256
2257 #[fuchsia::test]
2258 fn test_storage_allocator_bad_context() {
2259 let mut out_storage = zxio_storage_t::default();
2260 let mut out_storage_ptr = &mut out_storage as *mut zxio_storage_t;
2261
2262 let out_context = std::ptr::null_mut();
2263
2264 #[allow(
2265 clippy::undocumented_unsafe_blocks,
2266 reason = "Force documented unsafe blocks in Starnix"
2267 )]
2268 let out = unsafe {
2269 storage_allocator(
2270 0 as zxio_object_type_t,
2271 &mut out_storage_ptr as *mut *mut zxio_storage_t,
2272 out_context,
2273 )
2274 };
2275 assert_eq!(out, zx::sys::ZX_ERR_NO_MEMORY);
2276 }
2277
2278 #[fuchsia::test]
2279 fn test_parse_ip_pktinfo_control_message() {
2280 let pktinfo = zxio::in_pktinfo {
2281 ipi_ifindex: 1,
2282 ipi_spec_dst: zxio::in_addr { s_addr: u32::from_ne_bytes([192, 0, 2, 1]) },
2283 ipi_addr: zxio::in_addr { s_addr: u32::from_ne_bytes([192, 0, 2, 2]) },
2284 };
2285 let total_size = CMSG_HEADER_SIZE + size_of_val(&pktinfo);
2286 let header = zxio::cmsghdr {
2287 cmsg_len: total_size as c_uint,
2288 cmsg_level: zxio::SOL_IP as i32,
2289 cmsg_type: zxio::IP_PKTINFO as i32,
2290 };
2291 let mut out = vec![0u8; total_size];
2292 header.write_to_prefix(&mut out[..]).unwrap();
2293 pktinfo.write_to_prefix(&mut out[CMSG_HEADER_SIZE..]).unwrap();
2294 let parsed = parse_control_messages(&out);
2295 assert_eq!(
2296 parsed,
2297 vec![ControlMessage::IpPacketInfo {
2298 iface: 1,
2299 local_addr: [192, 0, 2, 1],
2300 header_destination_addr: [192, 0, 2, 2],
2301 }]
2302 );
2303 }
2304}