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