1use std::{
2 mem::MaybeUninit,
3 os::fd::{AsRawFd as _, OwnedFd},
4};
5
6use dope_sys::{Errno, io::file, kernel};
7use o3::{alloc::BoxArrayExt as _, base::marker::ThreadBound};
8
9use crate::Uring;
10
11const CAPACITY: usize = 32 * 1024;
12
13pub struct Stream {
14 file: OwnedFd,
15 buffer: Box<[MaybeUninit<u8>; CAPACITY]>,
16 offset: usize,
17 filled: usize,
18 _thread: ThreadBound,
19}
20
21unsafe impl file::raw::Directory for Uring {
24 type Stream = Stream;
25
26 fn from_fd<E: From<Errno>>(file: OwnedFd) -> Result<Stream, file::OpenError<E>> {
27 Ok(Stream {
28 file,
29 buffer: Box::try_uninit().map_err(file::OpenError::Allocation)?,
30 offset: 0,
31 filled: 0,
32 _thread: ThreadBound::NEW,
33 })
34 }
35
36 fn descriptor(stream: &Stream) -> kernel::raw::Descriptor {
37 unsafe { kernel::raw::Descriptor::new(stream.file.as_raw_fd()) }
39 }
40
41 fn next<E: From<Errno>>(
42 stream: &mut Stream,
43 ) -> Result<Option<file::Entry<'_>>, file::ReadError<E>> {
44 if stream.offset == stream.filled {
45 let result = unsafe {
48 libc::syscall(
49 libc::SYS_getdents64,
50 stream.file.as_raw_fd(),
51 stream.buffer.as_mut_ptr(),
52 CAPACITY,
53 )
54 };
55 let count = unsafe {
58 kernel::raw::Status::from_count(result as libc::ssize_t)
59 .try_count::<Errno>(|| kernel::raw::Code::last::<Self>().into())
60 };
61 stream.filled = match count {
62 Ok(count) => count.get(),
63 Err(Errno::NOT_FOUND) => 0,
64 Err(error) => return Err(file::ReadError::Syscall(E::from(error))),
65 };
66 stream.offset = 0;
67 if stream.filled == 0 {
68 return Ok(None);
69 }
70 }
71 let pointer = unsafe {
76 stream
77 .buffer
78 .as_ptr()
79 .add(stream.offset)
80 .cast::<libc::dirent64>()
81 };
82 stream.offset += unsafe { (&raw const (*pointer).d_reclen).read_unaligned() } as usize;
83 let kind = unsafe { (&raw const (*pointer).d_type).read() };
84 let name = unsafe { std::ffi::CStr::from_ptr((&raw const (*pointer).d_name).cast()) };
85 Ok(Some(file::Entry {
86 name,
87 kind: file::Kind::from_dirent(kind),
88 }))
89 }
90}