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