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