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