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::{Error as MetadataError, 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 Metadata;
121 type Request<'a>: Request<
122 'a,
123 Options = Self::Options,
124 Metadata = Self::Metadata,
125 Buffer = bytes::Prefix<'a, { bounded::MAX }>,
126 >
127 where
128 Self: 'a;
129 fn open<E: From<Errno>>(path: &std::ffi::CStr, flags: Open, mode: Mode) -> Result<OwnedFd, E> {
130 let raw = unsafe { libc::open(path.as_ptr(), flags.raw(), mode.raw() as libc::c_uint) };
131 unsafe {
132 <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
133 }
134 }
135
136 fn metadata<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<Metadata, E> {
137 let mut value = mem::MaybeUninit::uninit();
138 let raw = unsafe { libc::fstat(file.as_raw_fd(), value.as_mut_ptr()) };
139 unsafe {
141 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
142 }?;
143 Ok(Metadata::new(unsafe { value.assume_init() }))
144 }
145
146 fn stat<E: From<Errno>>(path: &std::ffi::CStr) -> Result<Metadata, E> {
147 let mut value = mem::MaybeUninit::uninit();
148 let raw = unsafe { libc::stat(path.as_ptr(), value.as_mut_ptr()) };
149 unsafe {
151 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
152 }?;
153 Ok(Metadata::new(unsafe { value.assume_init() }))
154 }
155
156 fn mkdir<E: From<Errno>>(path: &std::ffi::CStr, mode: Mode) -> Result<(), E> {
157 unsafe {
159 kernel::raw::Status::new(libc::mkdir(path.as_ptr(), mode.raw()))
160 .try_zero(|| kernel::raw::Code::last::<Self>().into())
161 }
162 }
163
164 fn metadata_at<E: From<Errno>>(
166 dir: Option<BorrowedFd<'_>>,
167 name: &std::ffi::CStr,
168 ) -> Result<Metadata, E> {
169 let mut value = mem::MaybeUninit::uninit();
170 let raw = unsafe {
172 libc::fstatat(
173 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
174 name.as_ptr(),
175 value.as_mut_ptr(),
176 libc::AT_SYMLINK_NOFOLLOW,
177 )
178 };
179 unsafe {
181 kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
182 }?;
183 Ok(Metadata::new(unsafe { value.assume_init() }))
184 }
185
186 fn unlink<E: From<Errno>>(path: &std::ffi::CStr) -> Result<(), E> {
187 unsafe {
189 kernel::raw::Status::new(libc::unlink(path.as_ptr()))
190 .try_zero(|| kernel::raw::Code::last::<Self>().into())
191 }
192 }
193
194 fn rename<E: From<Errno>>(from: &std::ffi::CStr, to: &std::ffi::CStr) -> Result<(), E> {
195 unsafe {
197 kernel::raw::Status::new(libc::rename(from.as_ptr(), to.as_ptr()))
198 .try_zero(|| kernel::raw::Code::last::<Self>().into())
199 }
200 }
201
202 fn try_lock<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
204 unsafe {
206 kernel::raw::Status::new(libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB))
207 .try_zero(|| kernel::raw::Code::last::<Self>().into())
208 }
209 }
210
211 fn sync<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
212 unsafe {
214 kernel::raw::Status::new(libc::fsync(file.as_raw_fd()))
215 .try_zero(|| kernel::raw::Code::last::<Self>().into())
216 }
217 }
218
219 fn truncate<E: From<Errno>>(file: BorrowedFd<'_>, len: i64) -> Result<(), E> {
220 unsafe {
222 kernel::raw::Status::new(libc::ftruncate(file.as_raw_fd(), len))
223 .try_zero(|| kernel::raw::Code::last::<Self>().into())
224 }
225 }
226
227 fn unlink_at<E: From<Errno>>(dir: BorrowedFd<'_>, name: &std::ffi::CStr) -> Result<(), E> {
228 unsafe {
230 kernel::raw::Status::new(libc::unlinkat(dir.as_raw_fd(), name.as_ptr(), 0))
231 .try_zero(|| kernel::raw::Code::last::<Self>().into())
232 }
233 }
234
235 fn remove_dir_at<E: From<Errno>>(
236 dir: Option<BorrowedFd<'_>>,
237 name: &std::ffi::CStr,
238 ) -> Result<(), E> {
239 unsafe {
242 kernel::raw::Status::new(libc::unlinkat(
243 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
244 name.as_ptr(),
245 libc::AT_REMOVEDIR,
246 ))
247 .try_zero(|| kernel::raw::Code::last::<Self>().into())
248 }
249 }
250
251 fn open_at<E: From<Errno>>(
252 dir: BorrowedFd<'_>,
253 name: &std::ffi::CStr,
254 flags: Open,
255 mode: Mode,
256 ) -> Result<OwnedFd, E> {
257 let raw = unsafe {
258 libc::openat(
259 dir.as_raw_fd(),
260 name.as_ptr(),
261 flags.raw(),
262 mode.raw() as libc::c_uint,
263 )
264 };
265 unsafe {
266 <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
267 }
268 }
269
270 fn rename_at<E: From<Errno>>(
271 dir: BorrowedFd<'_>,
272 old: &std::ffi::CStr,
273 new: &std::ffi::CStr,
274 ) -> Result<(), E> {
275 unsafe {
277 kernel::raw::Status::new(libc::renameat(
278 dir.as_raw_fd(),
279 old.as_ptr(),
280 dir.as_raw_fd(),
281 new.as_ptr(),
282 ))
283 .try_zero(|| kernel::raw::Code::last::<Self>().into())
284 }
285 }
286
287 fn read_at<'a, E: From<Errno>>(
289 file: BorrowedFd<'_>,
290 output: &'a mut [u8],
291 offset: Offset,
292 ) -> Result<&'a mut [u8], E> {
293 let output =
295 unsafe { std::slice::from_raw_parts_mut(output.as_mut_ptr().cast(), output.len()) };
296 raw::read_at::<Self, E>(file, output, offset)
297 }
298
299 fn read_uninit_at<'a, E: From<Errno>>(
300 file: BorrowedFd<'_>,
301 output: &'a mut [mem::MaybeUninit<u8>],
302 offset: Offset,
303 ) -> Result<&'a mut [u8], E> {
304 raw::read_at::<Self, E>(file, output, offset)
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}