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
104impl<S: raw::Directory> Entries<'_, S> {
105 pub fn try_next(&mut self) -> Result<Option<Entry<'_>>, Errno> {
107 S::next(self.0)
108 }
109}
110
111pub trait File: errno::Source + raw::Directory {
113 type Options;
114 type Metadata;
115 type Request<'a>: Request<
116 'a,
117 Options = Self::Options,
118 Metadata = Self::Metadata,
119 Buffer = bytes::Prefix<'a, { bounded::MAX }>,
120 >
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 { <OwnedFd as fd::raw::Adopt>::try_adopt::<Self, E>(raw) }
126 }
127
128 fn metadata<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<Metadata, E> {
129 let mut value = mem::MaybeUninit::uninit();
130 let raw = unsafe { libc::fstat(file.as_raw_fd(), value.as_mut_ptr()) };
131 unsafe { kernel::raw::Status::new(raw).try_zero::<Self, E>() }?;
133 Ok(Metadata::new(unsafe { value.assume_init() }))
134 }
135
136 fn stat<E: From<Errno>>(path: &std::ffi::CStr) -> Result<Metadata, E> {
137 let mut value = mem::MaybeUninit::uninit();
138 let raw = unsafe { libc::stat(path.as_ptr(), value.as_mut_ptr()) };
139 unsafe { kernel::raw::Status::new(raw).try_zero::<Self, E>() }?;
141 Ok(Metadata::new(unsafe { value.assume_init() }))
142 }
143
144 fn mkdir<E: From<Errno>>(path: &std::ffi::CStr, mode: Mode) -> Result<(), E> {
145 unsafe {
147 kernel::raw::Status::new(libc::mkdir(path.as_ptr(), mode.raw())).try_zero::<Self, E>()
148 }
149 }
150
151 fn metadata_at<E: From<Errno>>(
153 dir: Option<BorrowedFd<'_>>,
154 name: &std::ffi::CStr,
155 ) -> Result<Metadata, E> {
156 let mut value = mem::MaybeUninit::uninit();
157 let raw = unsafe {
159 libc::fstatat(
160 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
161 name.as_ptr(),
162 value.as_mut_ptr(),
163 libc::AT_SYMLINK_NOFOLLOW,
164 )
165 };
166 unsafe { kernel::raw::Status::new(raw).try_zero::<Self, E>() }?;
168 Ok(Metadata::new(unsafe { value.assume_init() }))
169 }
170
171 fn unlink<E: From<Errno>>(path: &std::ffi::CStr) -> Result<(), E> {
172 unsafe { kernel::raw::Status::new(libc::unlink(path.as_ptr())).try_zero::<Self, E>() }
174 }
175
176 fn rename<E: From<Errno>>(from: &std::ffi::CStr, to: &std::ffi::CStr) -> Result<(), E> {
177 unsafe {
179 kernel::raw::Status::new(libc::rename(from.as_ptr(), to.as_ptr())).try_zero::<Self, E>()
180 }
181 }
182
183 fn try_lock<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
185 unsafe {
187 kernel::raw::Status::new(libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB))
188 .try_zero::<Self, E>()
189 }
190 }
191
192 fn sync<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
193 unsafe { kernel::raw::Status::new(libc::fsync(file.as_raw_fd())).try_zero::<Self, E>() }
195 }
196
197 fn truncate<E: From<Errno>>(file: BorrowedFd<'_>, len: i64) -> Result<(), E> {
198 unsafe {
200 kernel::raw::Status::new(libc::ftruncate(file.as_raw_fd(), len)).try_zero::<Self, E>()
201 }
202 }
203
204 fn unlink_at<E: From<Errno>>(dir: BorrowedFd<'_>, name: &std::ffi::CStr) -> Result<(), E> {
205 unsafe {
207 kernel::raw::Status::new(libc::unlinkat(dir.as_raw_fd(), name.as_ptr(), 0))
208 .try_zero::<Self, E>()
209 }
210 }
211
212 fn remove_dir_at<E: From<Errno>>(
213 dir: Option<BorrowedFd<'_>>,
214 name: &std::ffi::CStr,
215 ) -> Result<(), E> {
216 unsafe {
219 kernel::raw::Status::new(libc::unlinkat(
220 dir.map_or(libc::AT_FDCWD, |dir| dir.as_raw_fd()),
221 name.as_ptr(),
222 libc::AT_REMOVEDIR,
223 ))
224 .try_zero::<Self, E>()
225 }
226 }
227
228 fn open_at<E: From<Errno>>(
229 dir: BorrowedFd<'_>,
230 name: &std::ffi::CStr,
231 flags: Open,
232 mode: Mode,
233 ) -> Result<OwnedFd, E> {
234 let raw = unsafe {
235 libc::openat(
236 dir.as_raw_fd(),
237 name.as_ptr(),
238 flags.raw(),
239 mode.raw() as libc::c_uint,
240 )
241 };
242 unsafe { <OwnedFd as fd::raw::Adopt>::try_adopt::<Self, E>(raw) }
243 }
244
245 fn rename_at<E: From<Errno>>(
246 dir: BorrowedFd<'_>,
247 old: &std::ffi::CStr,
248 new: &std::ffi::CStr,
249 ) -> Result<(), E> {
250 unsafe {
252 kernel::raw::Status::new(libc::renameat(
253 dir.as_raw_fd(),
254 old.as_ptr(),
255 dir.as_raw_fd(),
256 new.as_ptr(),
257 ))
258 .try_zero::<Self, E>()
259 }
260 }
261
262 fn read_at<'a, E: From<Errno>>(
264 file: BorrowedFd<'_>,
265 output: &'a mut [u8],
266 offset: Offset,
267 ) -> Result<&'a mut [u8], E> {
268 let output =
270 unsafe { std::slice::from_raw_parts_mut(output.as_mut_ptr().cast(), output.len()) };
271 raw::read_at::<Self, E>(file, output, offset)
272 }
273
274 fn read_uninit_at<'a, E: From<Errno>>(
275 file: BorrowedFd<'_>,
276 output: &'a mut [mem::MaybeUninit<u8>],
277 offset: Offset,
278 ) -> Result<&'a mut [u8], E> {
279 raw::read_at::<Self, E>(file, output, offset)
280 }
281
282 fn write_at<E: From<Errno>>(
283 file: BorrowedFd<'_>,
284 input: bytes::Prefix<'_, { bounded::MAX }>,
285 offset: Offset,
286 ) -> Result<U32<0, { bounded::MAX as u32 }>, E> {
287 use kernel::raw::Status;
288
289 const { assert!(libc::off_t::MAX as u64 == i64::MAX as u64) };
290 let input = input.as_slice();
291 let raw = unsafe {
293 libc::pwrite(
294 file.as_raw_fd(),
295 input.as_ptr().cast(),
296 input.len(),
297 offset.get() as libc::off_t,
298 )
299 };
300 Ok(unsafe { Status::from_count(raw).try_count::<Self, E>() }?.bounded())
303 }
304}