1use std::{
2 mem,
3 os::fd::{AsRawFd as _, BorrowedFd, OwnedFd},
4};
5
6use o3::{
7 alloc,
8 base::num::bounded::{U32, U64},
9 buffer::bytes::{self, bounded},
10};
11pub use opened::Opened;
12pub use sync::Sync;
13
14use crate::{
15 Errno,
16 io::{fd, request::Request},
17 kernel::{self, errno},
18};
19
20mod kind;
21mod metadata;
22mod mode;
23mod open;
24mod opened;
25pub mod raw;
26mod sync;
27
28pub use kind::Kind;
29pub use metadata::Metadata;
30pub use mode::Mode;
31pub use open::Open;
32
33#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
35#[repr(transparent)]
36pub struct Offset(U64<0, { i64::MAX as u64 }>);
37
38impl Offset {
39 pub const fn new(value: u64) -> Option<Self> {
40 match U64::new(value) {
41 Some(value) => Some(Self(value)),
42 None => None,
43 }
44 }
45
46 pub const fn clamp(value: u64) -> Self {
47 Self(U64::clamp(value))
48 }
49
50 pub const fn get(self) -> u64 {
51 self.0.get()
52 }
53
54 pub const fn checked_sub(self, value: u64) -> Option<Self> {
56 match self.0.checked_sub(value) {
57 Some(value) => Some(Self(value)),
58 None => None,
59 }
60 }
61}
62
63pub trait Completion {
65 type Opened: raw::Completion;
66}
67
68#[repr(transparent)]
70pub struct Dir<S: raw::Directory>(S::Stream);
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub enum OpenError<E> {
74 Syscall(E),
75 Allocation(alloc::Exhausted),
76}
77
78impl<S: File> Dir<S> {
79 pub fn open<E: From<Errno>>(path: &std::ffi::CStr) -> Result<Self, OpenError<E>> {
80 S::open_dir(path).map(Self)
81 }
82
83 pub fn from_fd<E: From<Errno>>(file: OwnedFd) -> Result<Self, OpenError<E>> {
84 S::from_fd(file).map(Self)
85 }
86
87 pub fn entries(&mut self) -> (BorrowedFd<'_>, Entries<'_, S>) {
88 let file = S::descriptor(&self.0);
89 (
90 unsafe { BorrowedFd::borrow_raw(file.get()) },
93 Entries(&mut self.0),
94 )
95 }
96}
97
98#[repr(transparent)]
99pub struct Entries<'a, S: raw::Directory>(&'a mut S::Stream);
100
101#[derive(Clone, Copy, Debug)]
103pub struct Entry<'a> {
104 pub name: &'a std::ffi::CStr,
105 pub kind: Option<Kind>,
106}
107
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum ReadError<E> {
110 Syscall(E),
111 Invalid(kernel::Invalid),
112}
113
114impl<S: raw::Directory> Entries<'_, S> {
115 pub fn try_next<E: From<Errno>>(&mut self) -> Result<Option<Entry<'_>>, ReadError<E>> {
117 S::next(self.0)
118 }
119}
120
121pub trait File: errno::Source + raw::Directory {
123 type Options;
124 type Request<'a>: Request<'a, Options = Self::Options, Buffer = bytes::Prefix<'a, { bounded::MAX }>>
125 where
126 Self: 'a;
127 fn open<E: From<Errno>>(path: &std::ffi::CStr, flags: Open, mode: Mode) -> Result<OwnedFd, E> {
128 let raw = unsafe { libc::open(path.as_ptr(), flags.raw(), mode.raw() as libc::c_uint) };
129 unsafe {
130 <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
131 }
132 }
133
134 fn metadata<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<Metadata, E> {
135 let mut value = mem::MaybeUninit::uninit();
136 let raw = unsafe { libc::fstat(file.as_raw_fd(), value.as_mut_ptr()) };
137 unsafe {
139 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
140 }?;
141 Ok(Metadata::new(unsafe { value.assume_init() }))
142 }
143
144 fn stat<E: From<Errno>>(path: &std::ffi::CStr) -> Result<Metadata, E> {
145 let mut value = mem::MaybeUninit::uninit();
146 let raw = unsafe { libc::stat(path.as_ptr(), value.as_mut_ptr()) };
147 unsafe {
149 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
150 }?;
151 Ok(Metadata::new(unsafe { value.assume_init() }))
152 }
153
154 fn mkdir<E: From<Errno>>(path: &std::ffi::CStr, mode: Mode) -> Result<(), E> {
155 unsafe {
157 kernel::raw::Status::new(libc::mkdir(path.as_ptr(), mode.raw()))
158 .try_zero(|| kernel::raw::Code::last::<Self>().into())
159 }
160 }
161
162 fn metadata_at<E: From<Errno>>(
164 dir: Option<BorrowedFd<'_>>,
165 name: &std::ffi::CStr,
166 ) -> Result<Metadata, E> {
167 let mut value = mem::MaybeUninit::uninit();
168 let raw = unsafe {
170 libc::fstatat(
171 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
172 name.as_ptr(),
173 value.as_mut_ptr(),
174 libc::AT_SYMLINK_NOFOLLOW,
175 )
176 };
177 unsafe {
179 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
180 }?;
181 Ok(Metadata::new(unsafe { value.assume_init() }))
182 }
183
184 fn unlink<E: From<Errno>>(path: &std::ffi::CStr) -> Result<(), E> {
185 unsafe {
187 kernel::raw::Status::new(libc::unlink(path.as_ptr()))
188 .try_zero(|| kernel::raw::Code::last::<Self>().into())
189 }
190 }
191
192 fn rename<E: From<Errno>>(from: &std::ffi::CStr, to: &std::ffi::CStr) -> Result<(), E> {
193 unsafe {
195 kernel::raw::Status::new(libc::rename(from.as_ptr(), to.as_ptr()))
196 .try_zero(|| kernel::raw::Code::last::<Self>().into())
197 }
198 }
199
200 fn try_lock<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
202 unsafe {
204 kernel::raw::Status::new(libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB))
205 .try_zero(|| kernel::raw::Code::last::<Self>().into())
206 }
207 }
208
209 fn sync<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
210 unsafe {
212 kernel::raw::Status::new(libc::fsync(file.as_raw_fd()))
213 .try_zero(|| kernel::raw::Code::last::<Self>().into())
214 }
215 }
216
217 fn truncate<E: From<Errno>>(file: BorrowedFd<'_>, len: i64) -> Result<(), E> {
218 unsafe {
220 kernel::raw::Status::new(libc::ftruncate(file.as_raw_fd(), len))
221 .try_zero(|| kernel::raw::Code::last::<Self>().into())
222 }
223 }
224
225 fn unlink_at<E: From<Errno>>(dir: BorrowedFd<'_>, name: &std::ffi::CStr) -> Result<(), E> {
226 unsafe {
228 kernel::raw::Status::new(libc::unlinkat(dir.as_raw_fd(), name.as_ptr(), 0))
229 .try_zero(|| kernel::raw::Code::last::<Self>().into())
230 }
231 }
232
233 fn remove_dir<E: From<Errno>>(
235 dir: Option<BorrowedFd<'_>>,
236 name: &std::ffi::CStr,
237 ) -> Result<(), E> {
238 unsafe {
240 kernel::raw::Status::new(libc::unlinkat(
241 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
242 name.as_ptr(),
243 libc::AT_REMOVEDIR,
244 ))
245 .try_zero(|| kernel::raw::Code::last::<Self>().into())
246 }
247 }
248
249 fn open_at<E: From<Errno>>(
250 dir: BorrowedFd<'_>,
251 name: &std::ffi::CStr,
252 flags: Open,
253 mode: Mode,
254 ) -> Result<OwnedFd, E> {
255 let raw = unsafe {
256 libc::openat(
257 dir.as_raw_fd(),
258 name.as_ptr(),
259 flags.raw(),
260 mode.raw() as libc::c_uint,
261 )
262 };
263 unsafe {
264 <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
265 }
266 }
267
268 fn rename_at<E: From<Errno>>(
269 dir: BorrowedFd<'_>,
270 old: &std::ffi::CStr,
271 new: &std::ffi::CStr,
272 ) -> Result<(), E> {
273 unsafe {
275 kernel::raw::Status::new(libc::renameat(
276 dir.as_raw_fd(),
277 old.as_ptr(),
278 dir.as_raw_fd(),
279 new.as_ptr(),
280 ))
281 .try_zero(|| kernel::raw::Code::last::<Self>().into())
282 }
283 }
284
285 fn read_at<'a, E: From<Errno>>(
287 file: BorrowedFd<'_>,
288 output: &'a mut [u8],
289 offset: Offset,
290 ) -> Result<&'a mut [u8], E> {
291 const { assert!(libc::off_t::MAX as u64 == i64::MAX as u64) };
292 let raw = unsafe {
295 libc::pread(
296 file.as_raw_fd(),
297 output.as_mut_ptr().cast(),
298 output.len(),
299 offset.get() as libc::off_t,
300 )
301 };
302 unsafe {
305 crate::io::raw::Buffer::try_native(output, raw, || {
306 kernel::raw::Code::last::<Self>().into()
307 })
308 }
309 }
310
311 fn write_at<E: From<Errno>>(
312 file: BorrowedFd<'_>,
313 input: bytes::Prefix<'_, { bounded::MAX }>,
314 offset: Offset,
315 ) -> Result<U32<0, { bounded::MAX as u32 }>, E> {
316 use kernel::raw::Status;
317
318 const { assert!(libc::off_t::MAX as u64 == i64::MAX as u64) };
319 let input = input.into_slice();
320 let raw = unsafe {
322 libc::pwrite(
323 file.as_raw_fd(),
324 input.as_ptr().cast(),
325 input.len(),
326 offset.get() as libc::off_t,
327 )
328 };
329 Ok(unsafe {
332 Status::from_count(raw).try_count::<E>(|| kernel::raw::Code::last::<Self>().into())
333 }?
334 .bounded())
335 }
336}