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(stream: &mut Stream) -> Result<Option<file::Entry<'_>>, Errno> {
42 if stream.offset == stream.filled {
43 let result = unsafe {
46 libc::syscall(
47 libc::SYS_getdents64,
48 stream.file.as_raw_fd(),
49 stream.buffer.as_mut_ptr(),
50 CAPACITY,
51 )
52 };
53 let count = unsafe {
56 kernel::raw::Status::from_count(result as libc::ssize_t).try_count::<Self, Errno>()
57 };
58 stream.filled = match count {
59 Ok(count) => count.get(),
60 Err(Errno::NOT_FOUND) => 0,
61 Err(error) => return Err(error),
62 };
63 stream.offset = 0;
64 if stream.filled == 0 {
65 return Ok(None);
66 }
67 }
68 let pointer = unsafe {
73 stream
74 .buffer
75 .as_ptr()
76 .add(stream.offset)
77 .cast::<libc::dirent64>()
78 };
79 stream.offset += unsafe { (&raw const (*pointer).d_reclen).read_unaligned() } as usize;
80 let kind = unsafe { (&raw const (*pointer).d_type).read() };
81 let name = unsafe { std::ffi::CStr::from_ptr((&raw const (*pointer).d_name).cast()) };
82 Ok(Some(file::Entry {
83 name,
84 kind: file::Kind::from_dirent(kind),
85 }))
86 }
87}