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