1use std::{fs, mem, os::fd};
2
3use dope_sys::io::{fd::Fd as _, file, request::Request};
4use file::File as _;
5use o3::{base::marker::ThreadBound, buffer::bytes};
6
7use crate::{
8 Backend, Errno,
9 driver::{
10 route,
11 route::{
12 kind,
13 kind::{Class, Open, Write},
14 },
15 },
16 io::{fs::metadata::Metadata, transfer},
17};
18
19mod dir;
20
21mod len;
22pub mod metadata;
23pub mod open;
24pub mod path;
25
26pub use dir::Dir;
27pub use len::Len;
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31#[repr(transparent)]
32pub struct Kind(file::Kind);
33
34impl Kind {
35 pub const fn is_regular(self) -> bool {
36 self.0.is_regular()
37 }
38}
39
40impl From<file::Kind> for Kind {
41 fn from(kind: file::Kind) -> Self {
42 Self(kind)
43 }
44}
45
46#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
48#[repr(transparent)]
49pub struct Offset(file::Offset);
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
53pub struct Overflow {
54 pub actual: u64,
55 pub maximum: u64,
56}
57
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub struct Extent {
60 offset: Offset,
61 length: transfer::Len,
62}
63
64impl From<Len> for Offset {
65 fn from(length: Len) -> Self {
66 Self(length.0)
67 }
68}
69
70impl From<transfer::Len> for Offset {
71 fn from(length: transfer::Len) -> Self {
72 Self(file::Offset::clamp(length.into_raw() as u64))
73 }
74}
75
76impl Offset {
77 pub const fn fixed<const OFFSET: u64>() -> Self {
78 const { assert!(OFFSET <= i64::MAX as u64) };
79 Self(file::Offset::clamp(OFFSET))
80 }
81
82 pub const fn try_new(value: u64) -> Result<Self, Overflow> {
83 match file::Offset::new(value) {
84 Some(value) => Ok(Self(value)),
85 None => Err(Overflow {
86 actual: value,
87 maximum: i64::MAX as u64,
88 }),
89 }
90 }
91
92 pub const fn get(self) -> u64 {
93 self.0.get()
94 }
95
96 pub const fn checked_add(self, length: transfer::Len) -> Result<Self, Extent> {
97 let value = self.get() + length.into_raw() as u64;
98 match Self::try_new(value) {
99 Ok(value) => Ok(value),
100 Err(_) => Err(Extent::new(self, length)),
101 }
102 }
103
104 pub const fn checked_sub(self, length: transfer::Len) -> Result<Self, Extent> {
105 match self.0.checked_sub(length.into_raw() as u64) {
106 Some(value) => Ok(Self(value)),
107 None => Err(Extent::new(self, length)),
108 }
109 }
110}
111
112impl Extent {
113 const fn new(offset: Offset, length: transfer::Len) -> Self {
114 Self { offset, length }
115 }
116}
117
118const _: () = {
119 assert!(mem::size_of::<Kind>() == mem::size_of::<file::Kind>());
120 assert!(mem::align_of::<Kind>() == mem::align_of::<file::Kind>());
121 assert!(mem::size_of::<Offset>() == mem::size_of::<u64>());
122 assert!(mem::align_of::<Offset>() == mem::align_of::<u64>());
123 assert!(mem::size_of::<Extent>() == 2 * mem::size_of::<u64>());
124 assert!(mem::align_of::<Extent>() == mem::align_of::<u64>());
125 assert!(mem::size_of::<Result<Offset, Extent>>() == 3 * mem::size_of::<u64>());
126 assert!(mem::align_of::<Result<Offset, Extent>>() == mem::align_of::<u64>());
127};
128
129pub struct Submission<'a, 'd, Tag, C = <Tag as route::Tag>::Class>
137where
138 Tag: route::Tag,
139 C: Class,
140{
141 raw: <Backend as file::File>::Request<'a>,
142 target: route::Operation<'d, Tag, C>,
143}
144
145pub struct OpenRequest {
146 path: path::Confined,
147 beneath: <Backend as file::File>::Options,
148}
149
150impl<'a, 'd, Tag, C> Submission<'a, 'd, Tag, C>
151where
152 Tag: route::Tag,
153 C: Class,
154{
155 fn bind(
156 raw: <Backend as file::File>::Request<'a>,
157 target: route::Operation<'d, Tag, C>,
158 ) -> Self {
159 Self { raw, target }
160 }
161
162 pub(crate) fn into_parts(
163 self,
164 ) -> (
165 <Backend as file::File>::Request<'a>,
166 route::Operation<'d, Tag, C>,
167 ) {
168 (self.raw, self.target)
169 }
170}
171
172impl<'a, 'd, Tag> Submission<'a, 'd, Tag, Write>
173where
174 Tag: route::Tag,
175{
176 #[doc(hidden)]
177 pub fn write_prefix(
178 fd: fd::BorrowedFd<'a>,
179 buffer: &'a [u8],
180 offset: Offset,
181 target: route::Target<'d, Tag>,
182 ) -> Self
183 where
184 Tag: route::Select<Write>,
185 {
186 let input = bytes::Prefix::new(buffer);
187 Self::bind(
188 Request::write(fd, input, offset.0),
189 target.operation::<Write>(),
190 )
191 }
192
193 #[doc(hidden)]
196 pub fn write_current_prefix(
197 fd: fd::BorrowedFd<'a>,
198 buffer: bytes::Prefix<'a, { transfer::MAX_BYTES }>,
199 target: route::Target<'d, Tag>,
200 ) -> Self
201 where
202 Tag: route::Select<Write>,
203 {
204 Self::bind(
205 Request::write_current(fd, buffer),
206 target.operation::<Write>(),
207 )
208 }
209}
210
211impl<'a, 'd, Tag> Submission<'a, 'd, Tag, kind::Read>
212where
213 Tag: route::Tag,
214{
215 pub fn read_at(
219 fd: fd::BorrowedFd<'a>,
220 output: bytes::bounded::Value<&'a mut [mem::MaybeUninit<u8>]>,
221 offset: Offset,
222 target: route::Target<'d, Tag>,
223 ) -> Self
224 where
225 Tag: route::Select<kind::Read>,
226 {
227 Self::bind(
228 Request::read(fd, output, offset.0),
229 target.operation::<kind::Read>(),
230 )
231 }
232}
233
234impl<'a, 'd, Tag> Submission<'a, 'd, Tag, kind::Sync>
235where
236 Tag: route::Tag,
237{
238 pub fn metadata(
239 fd: fd::BorrowedFd<'a>,
240 output: &'a mut metadata::Output,
241 target: route::Target<'d, Tag>,
242 ) -> Self
243 where
244 Tag: route::Select<kind::Sync>,
245 {
246 Self::bind(
247 Request::metadata(fd, output),
248 target.operation::<kind::Sync>(),
249 )
250 }
251
252 pub fn sync(fd: fd::BorrowedFd<'a>, mode: Sync, target: route::Target<'d, Tag>) -> Self
254 where
255 Tag: route::Select<kind::Sync>,
256 {
257 Self::bind(
258 Request::sync(
259 fd,
260 match mode {
261 Sync::Data => file::Sync::Data,
262 Sync::All => file::Sync::All,
263 },
264 ),
265 target.operation::<kind::Sync>(),
266 )
267 }
268
269 #[doc(hidden)]
271 pub fn rename(rename: path::Rename<'a>, target: route::Target<'d, Tag>) -> Self
272 where
273 Tag: route::Select<kind::Sync>,
274 {
275 Self::bind(
276 Request::rename(
277 fd::AsFd::as_fd(rename.directory()),
278 rename.old(),
279 rename.new_name(),
280 ),
281 target.operation::<kind::Sync>(),
282 )
283 }
284
285 #[doc(hidden)]
287 pub fn unlink(entry: path::Entry<'a>, target: route::Target<'d, Tag>) -> Self
288 where
289 Tag: route::Select<kind::Sync>,
290 {
291 Self::bind(
292 Request::unlink(fd::AsFd::as_fd(entry.directory()), entry.name()),
293 target.operation::<kind::Sync>(),
294 )
295 }
296}
297
298impl OpenRequest {
299 pub fn new(path: path::Confined, options: open::Options) -> Self {
301 let (flags, mode) = options.nofollow().lower();
302 Self {
303 path,
304 beneath: <<Backend as file::File>::Request<'_> as Request>::options(flags, mode),
305 }
306 }
307
308 pub fn submission<'d, Tag>(
309 &self,
310 target: route::Target<'d, Tag>,
311 ) -> Submission<'_, 'd, Tag, Open>
312 where
313 Tag: route::Tag + route::Select<Open>,
314 {
315 let path = self.path.view();
316 Submission::bind(
317 Request::open(
318 fd::AsFd::as_fd(path.directory()),
319 path.name(),
320 &self.beneath,
321 ),
322 target.operation::<Open>(),
323 )
324 }
325
326 pub fn into_path(self) -> path::Confined {
327 self.path
328 }
329}
330
331pub enum Sync {
333 Data,
334 All,
335}
336
337#[repr(transparent)]
339pub struct File {
340 fd: fd::OwnedFd,
341 thread: ThreadBound,
342}
343
344impl File {
345 pub fn try_read<'a>(&self, bytes: &'a mut [u8]) -> Result<&'a mut [u8], Errno> {
346 Backend::read::<dope_sys::Errno>(fd::AsFd::as_fd(self), bytes)
347 }
348
349 pub fn try_read_at<'a>(
351 &self,
352 bytes: &'a mut [u8],
353 offset: Offset,
354 ) -> Result<&'a mut [u8], Errno> {
355 Backend::read_at::<dope_sys::Errno>(fd::AsFd::as_fd(self), bytes, offset.0)
356 }
357
358 pub fn try_write<'a>(&self, bytes: &'a [u8]) -> Result<&'a [u8], Errno> {
359 Backend::write::<dope_sys::Errno>(fd::AsFd::as_fd(self), bytes)
360 }
361
362 pub fn metadata(&self) -> Result<Metadata, Errno> {
363 let value = Backend::metadata::<dope_sys::Errno>(fd::AsFd::as_fd(self))?;
364 Ok(Metadata {
365 kind: Kind::from(value.kind()),
366 length: value.size(),
367 })
368 }
369
370 pub fn try_lock(&self) -> Result<(), Errno> {
372 Backend::try_lock::<dope_sys::Errno>(fd::AsFd::as_fd(self))
373 }
374
375 pub fn try_sync(&self) -> Result<(), Errno> {
376 Backend::sync::<dope_sys::Errno>(fd::AsFd::as_fd(self))
377 }
378}
379
380impl fd::AsFd for File {
381 fn as_fd(&self) -> fd::BorrowedFd<'_> {
382 self.fd.as_fd()
383 }
384}
385
386impl From<fd::OwnedFd> for File {
387 fn from(file: fd::OwnedFd) -> Self {
388 Self {
389 fd: file,
390 thread: ThreadBound::NEW,
391 }
392 }
393}
394
395impl From<fs::File> for File {
396 fn from(file: fs::File) -> Self {
397 Self {
398 fd: file.into(),
399 thread: ThreadBound::NEW,
400 }
401 }
402}
403
404impl From<File> for fs::File {
405 fn from(file: File) -> Self {
406 file.fd.into()
407 }
408}
409
410impl From<File> for fd::OwnedFd {
411 fn from(file: File) -> Self {
412 file.fd
413 }
414}
415
416const _: () = {
417 assert!(size_of::<File>() == size_of::<fd::OwnedFd>());
418 assert!(align_of::<File>() == align_of::<fd::OwnedFd>());
419};