nuclei/fs/file/positional.rs
1//! Reads and writes at offsets of the caller's own: [`Handle::read_at`],
2//! [`Handle::write_at`] and their forms. They never read or move the
3//! handle's cursor or its epoch, any number of them run at once on one
4//! handle, and they keep these rules with the calls at the cursor:
5//!
6//! - A positional call first waits for the cursor writes accepted before it
7//! (the write in flight and the write-behind) and for the positional
8//! writes left by their tasks: a positional write is never lost under, or
9//! reordered with, a write that came before it, and a positional read sees
10//! those writes.
11//! - When a positional write's task takes its end, the file's generation
12//! moves on: a cursor read made after it sees what it wrote, since a read
13//! job or bytes read ahead count only in the generation they began in. A
14//! positional write still out while a cursor read is taken races it, as
15//! `pwrite` and `pread` from two threads race.
16//! - A positional write whose future is dropped while it is out still lands;
17//! the file keeps it until it did (`orphans`).
18//! - The writes of a range (`space`: `allocate`; `copy`: the write side of
19//! `copy_range_to`) are positional writes in all this, and so is the cut of
20//! `set_len`, which moves the generation on when it ran.
21//!
22//! On io_uring each is a read or a write of the shard's ring at its offset;
23//! elsewhere a `pread` or `pwrite` on the blocking pool (on Windows a
24//! `ReadFile` or `WriteFile` at the offset its `OVERLAPPED` names).
25//! `proofs/tla/lent/LentFile.tla` (scenario "positional") checks the rules.
26
27use crate::buf::owned::{IoBuf, IoBufMut, OwnedBuf};
28use core::future::poll_fn;
29use core::task::{Context, Poll, Waker};
30use std::fs::File as StdFile;
31use std::io;
32use std::sync::Arc;
33
34use super::FileIo;
35use super::orphans::Orphan;
36use super::work::{Counted, Ended, Work, bounce, tally_read, tally_write};
37use crate::fs::call::End;
38use crate::io::Handle;
39use crate::io::side::After;
40
41impl FileIo {
42 /// Ready once every cursor write accepted before the call, and every
43 /// positional write left by its task, landed.
44 pub(super) fn poll_ordered(self: &Arc<Self>, cx: &mut Context<'_>) -> Poll<()> {
45 if !self.has_unsent() {
46 return Poll::Ready(());
47 }
48 self.with_state(cx, |st, io, waker, after| st.poll_writes(io, waker, after))
49 }
50
51 /// A buffer of the pool for the bytes of a task's memory at `addr`, and
52 /// where in it and how many of `len` the call moves (see `work::bounce`).
53 pub(super) async fn bounce_for(
54 &self,
55 len: usize,
56 addr: usize,
57 ) -> (crate::buf::PoolBuf, usize, usize) {
58 poll_fn(|cx| match bounce(len, addr, self.direct, cx.waker()) {
59 Some(got) => Poll::Ready(got),
60 None => Poll::Pending,
61 })
62 .await
63 }
64
65 pub(crate) async fn read_at(self: &Arc<Self>, out: &mut [u8], at: u64) -> io::Result<usize> {
66 if out.is_empty() {
67 return Ok(0);
68 }
69 poll_fn(|cx| self.poll_ordered(cx)).await;
70 let (buf, from, n) = self.bounce_for(out.len(), out.as_ptr() as usize).await;
71 let mut job = self.submit_read(buf, from, n, at);
72 let pooled = job.pooled();
73 let result = poll_fn(|cx| job.poll(cx.waker())).await;
74 tally_read(pooled, &result, |buf| buf.filled().len());
75 Ok(result?.peek_into(out))
76 }
77
78 pub(crate) async fn write_at(self: &Arc<Self>, bytes: &[u8], at: u64) -> io::Result<usize> {
79 if bytes.is_empty() {
80 return Ok(0);
81 }
82 poll_fn(|cx| self.poll_ordered(cx)).await;
83 let (mut buf, from, n) = self.bounce_for(bytes.len(), bytes.as_ptr() as usize).await;
84 buf.fill_at(from, &bytes[..n]);
85 let (work, bytes) = self.submit_pwrite(buf, at, Waker::noop());
86 let mut out = Out {
87 io: self,
88 work: Some(work),
89 bytes,
90 };
91 poll_fn(|cx| out.poll(cx)).await.map(|()| n)
92 }
93
94 pub(crate) async fn read_at_owned<B>(
95 self: &Arc<Self>,
96 buf: B,
97 at: u64,
98 ) -> (io::Result<usize>, B)
99 where
100 B: IoBufMut,
101 {
102 let buf = OwnedBuf::filled(buf);
103 if buf.len() == buf.capacity() {
104 return (Ok(0), buf.into_filled());
105 }
106 poll_fn(|cx| self.poll_ordered(cx)).await;
107 let mut job = self.submit_read_owned(&mut Some(buf), at, Waker::noop());
108 let pooled = job.pooled();
109 let (result, buf) = poll_fn(|cx| job.poll(cx.waker())).await;
110 tally_read(pooled, &result, |n| *n);
111 (result, buf.into_filled())
112 }
113
114 pub(crate) async fn write_at_owned<B>(
115 self: &Arc<Self>,
116 buf: B,
117 at: u64,
118 ) -> (io::Result<usize>, B)
119 where
120 B: IoBuf,
121 {
122 let buf = OwnedBuf::written(buf);
123 if buf.is_empty() {
124 return (Ok(0), buf.into_written());
125 }
126 poll_fn(|cx| self.poll_ordered(cx)).await;
127 let len = buf.len();
128 let (work, bytes) = self.submit_pwrite_owned(buf, at, Waker::noop());
129 let mut out = Out {
130 io: self,
131 work: Some(work),
132 bytes,
133 };
134 let (result, buf) = poll_fn(|cx| out.poll(cx)).await;
135 (result.map(|()| len), buf.into_written())
136 }
137
138 /// The file may hold other bytes than before (a write ended, failed or
139 /// not; a cut, a hole): reads of the file before this, read ahead or still
140 /// out, are of an older generation.
141 pub(crate) fn moved_on(&self) {
142 // Release: pairs with the Acquire of `FileIo::generation`.
143 self.generation
144 .fetch_add(1, core::sync::atomic::Ordering::Release);
145 }
146
147 /// Leaves `orphan`, a positional write whose task went away, to the
148 /// file state, which keeps everything after it waiting until it landed.
149 fn orphan(self: &Arc<Self>, orphan: Orphan) {
150 self.orphans.push(orphan);
151 let mut after = After::default();
152 self.state.defer(
153 super::deferred::ORPHAN,
154 &mut after,
155 &super::FileHooks { io: self },
156 );
157 // A call after this one looks at the state: its holder keeps the
158 // flag up while the write is out.
159 self.unsent
160 .store(true, core::sync::atomic::Ordering::Release);
161 after.run();
162 }
163}
164
165/// A positional write out, whose task takes its end. Dropped first, the
166/// write is left to the file. `bytes` are counted when it ends, as for a
167/// cursor write (see [`Writing`](super::work::Writing)): 0 for a ring's, or
168/// for a change of a range.
169pub(super) struct Out<'a, T>
170where
171 (Work<T>, Counted): Into<Orphan>,
172{
173 pub(super) io: &'a Arc<FileIo>,
174 pub(super) work: Option<Work<T>>,
175 pub(super) bytes: Counted,
176}
177
178impl<T> Out<'_, T>
179where
180 T: Ended + End,
181 (Work<T>, Counted): Into<Orphan>,
182{
183 /// The call's end, once it came.
184 pub(super) async fn finish(mut self) -> T {
185 poll_fn(|cx| self.poll(cx)).await
186 }
187
188 pub(super) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> {
189 let Some(work) = self.work.as_mut() else {
190 return Poll::Pending;
191 };
192 let end = core::task::ready!(work.poll(cx.waker()));
193 self.work = None;
194 tally_write(self.bytes, end.error().is_none());
195 self.io.moved_on();
196 Poll::Ready(end)
197 }
198}
199
200impl<T> Drop for Out<'_, T>
201where
202 (Work<T>, Counted): Into<Orphan>,
203{
204 fn drop(&mut self) {
205 if let Some(work) = self.work.take() {
206 self.io.orphan((work, self.bytes).into());
207 }
208 }
209}
210
211impl Handle<StdFile> {
212 /// Reads at `offset` into `buf` and answers how many bytes came, zero at
213 /// the end of the file, as std's `FileExt::read_at` does (on Windows
214 /// `seek_read`, after which the system's own position of the file is
215 /// past the bytes read, as std documents; the handle's cursor never
216 /// moves).
217 ///
218 /// The handle's cursor and the reads and writes at it are left as they
219 /// are, and any number of positional calls may run at once on one
220 /// handle. The read sees every write accepted at the cursor before it,
221 /// and every positional write that returned before it.
222 ///
223 /// The bytes come through a buffer of the runtime's pool, at most 64 KiB
224 /// a call (a short read, as `read_at` may answer). On a file opened for
225 /// direct IO the call reads exactly `buf.len()` bytes at `offset`, into
226 /// memory placed in its page as `buf` is, so the kernel refuses (with
227 /// `EINVAL`) exactly what it would refuse of `buf` itself.
228 /// [`read_at_owned`](Self::read_at_owned) reads into the caller's own
229 /// buffer with no copy.
230 ///
231 /// # Errors
232 ///
233 /// As the system's positional read: on a stream (a pipe, a terminal) the
234 /// file has no offset to read at.
235 pub async fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
236 self.file()?.read_at(buf, offset).await
237 }
238
239 /// Writes `buf` at `offset` and answers how many bytes landed, as std's
240 /// `FileExt::write_at` does (on Windows `seek_write`, after which the
241 /// system's own position of the file is past the bytes written; the
242 /// handle's cursor never moves). On Linux a file opened to append takes
243 /// the bytes at its end whatever the offset, as `pwrite(2)` does there.
244 ///
245 /// The write lands after every write accepted at the cursor before it,
246 /// and before any write the handle accepts after it returned; positional
247 /// writes running at once land in any order, as `pwrite` from two
248 /// threads does. If the future is dropped while the write is out, it
249 /// still lands, and every later call of the handle waits for it.
250 ///
251 /// The bytes go through a buffer of the runtime's pool, at most 64 KiB a
252 /// call (a short write, as `write_at` may answer). On a file opened for
253 /// direct IO the call writes exactly `buf` at `offset`, from memory
254 /// placed in its page as `buf` is, so the kernel refuses (with `EINVAL`)
255 /// exactly what it would refuse of `buf` itself.
256 /// [`write_at_owned`](Self::write_at_owned) writes from the caller's own
257 /// buffer with no copy.
258 ///
259 /// # Errors
260 ///
261 /// As the system's positional write.
262 pub async fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
263 self.file()?.write_at(buf, offset).await
264 }
265
266 /// Reads exactly `buf.len()` bytes at `offset`, as std's
267 /// `FileExt::read_exact_at` does, with [`read_at`](Self::read_at).
268 ///
269 /// # Errors
270 ///
271 /// `UnexpectedEof` when the file ends first (the bytes read so far are in
272 /// `buf`), or the first error of a read.
273 pub async fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
274 while !buf.is_empty() {
275 match self.read_at(buf, offset).await {
276 Ok(0) => {
277 return Err(io::Error::new(
278 io::ErrorKind::UnexpectedEof,
279 "failed to fill whole buffer",
280 ));
281 }
282 Ok(n) => {
283 buf = &mut buf[n..];
284 offset += n as u64;
285 }
286 Err(err) if err.kind() == io::ErrorKind::Interrupted => {}
287 Err(err) => return Err(err),
288 }
289 }
290 Ok(())
291 }
292
293 /// Writes all of `buf` at `offset`, as std's `FileExt::write_all_at`
294 /// does, with [`write_at`](Self::write_at).
295 ///
296 /// # Errors
297 ///
298 /// `WriteZero` when a write takes nothing, or the first error of a write.
299 pub async fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
300 while !buf.is_empty() {
301 match self.write_at(buf, offset).await {
302 Ok(0) => {
303 return Err(io::Error::new(
304 io::ErrorKind::WriteZero,
305 "failed to write whole buffer",
306 ));
307 }
308 Ok(n) => {
309 buf = &buf[n..];
310 offset += n as u64;
311 }
312 Err(err) if err.kind() == io::ErrorKind::Interrupted => {}
313 Err(err) => return Err(err),
314 }
315 }
316 Ok(())
317 }
318
319 /// Reads at `offset` into the spare capacity of `buf`, after the bytes
320 /// it holds, in one call of the system's positional read, and gives
321 /// `buf` back with how many bytes came: zero at the end of the file, or
322 /// when `buf` has no spare capacity. The bytes are read into `buf`
323 /// itself, never copied; on a file opened for direct IO the kernel's
324 /// alignment rules apply to `buf`'s own memory, which an
325 /// [`AlignedBuf`](crate::io::AlignedBuf) aligns. `buf` is any
326 /// [`IoBufMut`](crate::io::IoBufMut). Otherwise as
327 /// [`read_at`](Self::read_at).
328 pub async fn read_at_owned<B>(&self, buf: B, offset: u64) -> (io::Result<usize>, B)
329 where
330 B: IoBufMut,
331 {
332 match self.file() {
333 Ok(file) => file.read_at_owned(buf, offset).await,
334 Err(err) => (Err(err), buf),
335 }
336 }
337
338 /// Writes all of `buf` at `offset` and gives it back with its length
339 /// once the bytes landed. They are written from `buf` itself, never
340 /// copied; on a file opened for direct IO the kernel's alignment rules
341 /// apply to `buf`'s own memory, which an
342 /// [`AlignedBuf`](crate::io::AlignedBuf) aligns. If the future is dropped
343 /// while the write is out, it still lands, `buf` going with it. `buf` is
344 /// any [`IoBuf`](crate::io::IoBuf). Otherwise as
345 /// [`write_at`](Self::write_at).
346 pub async fn write_at_owned<B>(&self, buf: B, offset: u64) -> (io::Result<usize>, B)
347 where
348 B: IoBuf,
349 {
350 match self.file() {
351 Ok(file) => file.write_at_owned(buf, offset).await,
352 Err(err) => (Err(err), buf),
353 }
354 }
355}