tokio_uring/fs/file.rs
1use crate::buf::fixed::FixedBuf;
2use crate::buf::{BoundedBuf, BoundedBufMut, IoBuf, IoBufMut, Slice};
3use crate::fs::OpenOptions;
4use crate::io::SharedFd;
5
6use crate::runtime::driver::op::Op;
7use crate::{UnsubmittedOneshot, UnsubmittedWrite};
8use std::fmt;
9use std::io;
10use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
11use std::path::Path;
12
13/// A reference to an open file on the filesystem.
14///
15/// An instance of a `File` can be read and/or written depending on what options
16/// it was opened with. The `File` type provides **positional** read and write
17/// operations. The file does not maintain an internal cursor. The caller is
18/// required to specify an offset when issuing an operation.
19///
20/// While files are automatically closed when they go out of scope, the
21/// operation happens asynchronously in the background. It is recommended to
22/// call the `close()` function in order to guarantee that the file successfully
23/// closed before exiting the scope. Closing a file does not guarantee writes
24/// have persisted to disk. Use [`sync_all`] to ensure all writes have reached
25/// the filesystem.
26///
27/// [`sync_all`]: File::sync_all
28///
29/// # Examples
30///
31/// Creates a new file and write data to it:
32///
33/// ```no_run
34/// use tokio_uring::fs::File;
35///
36/// fn main() -> Result<(), Box<dyn std::error::Error>> {
37/// tokio_uring::start(async {
38/// // Open a file
39/// let file = File::create("hello.txt").await?;
40///
41/// // Write some data
42/// let (res, buf) = file.write_at(&b"hello world"[..], 0).submit().await;
43/// let n = res?;
44///
45/// println!("wrote {} bytes", n);
46///
47/// // Sync data to the file system.
48/// file.sync_all().await?;
49///
50/// // Close the file
51/// file.close().await?;
52///
53/// Ok(())
54/// })
55/// }
56/// ```
57pub struct File {
58 /// Open file descriptor
59 pub(crate) fd: SharedFd,
60}
61
62impl File {
63 /// Attempts to open a file in read-only mode.
64 ///
65 /// See the [`OpenOptions::open`] method for more details.
66 ///
67 /// # Errors
68 ///
69 /// This function will return an error if `path` does not already exist.
70 /// Other errors may also be returned according to [`OpenOptions::open`].
71 ///
72 /// # Examples
73 ///
74 /// ```no_run
75 /// use tokio_uring::fs::File;
76 ///
77 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
78 /// tokio_uring::start(async {
79 /// let f = File::open("foo.txt").await?;
80 ///
81 /// // Close the file
82 /// f.close().await?;
83 /// Ok(())
84 /// })
85 /// }
86 /// ```
87 pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
88 OpenOptions::new().read(true).open(path).await
89 }
90
91 /// Opens a file in write-only mode.
92 ///
93 /// This function will create a file if it does not exist,
94 /// and will truncate it if it does.
95 ///
96 /// See the [`OpenOptions::open`] function for more details.
97 ///
98 /// # Examples
99 ///
100 /// ```no_run
101 /// use tokio_uring::fs::File;
102 ///
103 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
104 /// tokio_uring::start(async {
105 /// let f = File::create("foo.txt").await?;
106 ///
107 /// // Close the file
108 /// f.close().await?;
109 /// Ok(())
110 /// })
111 /// }
112 /// ```
113 pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
114 OpenOptions::new()
115 .write(true)
116 .create(true)
117 .truncate(true)
118 .open(path)
119 .await
120 }
121
122 pub(crate) fn from_shared_fd(fd: SharedFd) -> File {
123 File { fd }
124 }
125
126 /// Converts a [`std::fs::File`][std] to a [`tokio_uring::fs::File`][file].
127 ///
128 /// [std]: std::fs::File
129 /// [file]: File
130 pub fn from_std(file: std::fs::File) -> File {
131 File::from_shared_fd(SharedFd::new(file.into_raw_fd()))
132 }
133
134 /// Read some bytes at the specified offset from the file into the specified
135 /// buffer, returning how many bytes were read.
136 ///
137 /// # Return
138 ///
139 /// The method returns the operation result and the same buffer value passed
140 /// as an argument.
141 ///
142 /// If the method returns [`Ok(n)`], then the read was successful. A nonzero
143 /// `n` value indicates that the buffer has been filled with `n` bytes of
144 /// data from the file. If `n` is `0`, then one of the following happened:
145 ///
146 /// 1. The specified offset is the end of the file.
147 /// 2. The buffer specified was 0 bytes in length.
148 ///
149 /// It is not an error if the returned value `n` is smaller than the buffer
150 /// size, even when the file contains enough data to fill the buffer.
151 ///
152 /// # Errors
153 ///
154 /// If this function encounters any form of I/O or other error, an error
155 /// variant will be returned. The buffer is returned on error.
156 ///
157 /// # Examples
158 ///
159 /// ```no_run
160 /// use tokio_uring::fs::File;
161 ///
162 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
163 /// tokio_uring::start(async {
164 /// let f = File::open("foo.txt").await?;
165 /// let buffer = vec![0; 10];
166 ///
167 /// // Read up to 10 bytes
168 /// let (res, buffer) = f.read_at(buffer, 0).await;
169 /// let n = res?;
170 ///
171 /// println!("The bytes: {:?}", &buffer[..n]);
172 ///
173 /// // Close the file
174 /// f.close().await?;
175 /// Ok(())
176 /// })
177 /// }
178 /// ```
179 pub async fn read_at<T: BoundedBufMut>(&self, buf: T, pos: u64) -> crate::BufResult<usize, T> {
180 // Submit the read operation
181 let op = Op::read_at(&self.fd, buf, pos).unwrap();
182 op.await
183 }
184
185 /// Read some bytes at the specified offset from the file into the specified
186 /// array of buffers, returning how many bytes were read.
187 ///
188 /// # Return
189 ///
190 /// The method returns the operation result and the same array of buffers
191 /// passed as an argument.
192 ///
193 /// If the method returns [`Ok(n)`], then the read was successful. A nonzero
194 /// `n` value indicates that the buffers have been filled with `n` bytes of
195 /// data from the file. If `n` is `0`, then one of the following happened:
196 ///
197 /// 1. The specified offset is the end of the file.
198 /// 2. The buffers specified were 0 bytes in length.
199 ///
200 /// It is not an error if the returned value `n` is smaller than the buffer
201 /// size, even when the file contains enough data to fill the buffer.
202 ///
203 /// # Errors
204 ///
205 /// If this function encounters any form of I/O or other error, an error
206 /// variant will be returned. The buffer is returned on error.
207 ///
208 /// # Examples
209 ///
210 /// ```no_run
211 /// use tokio_uring::fs::File;
212 ///
213 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
214 /// tokio_uring::start(async {
215 /// let f = File::open("foo.txt").await?;
216 /// let buffers = vec![Vec::<u8>::with_capacity(10), Vec::<u8>::with_capacity(10)];
217 ///
218 /// // Read up to 20 bytes
219 /// let (res, buffer) = f.readv_at(buffers, 0).await;
220 /// let n = res?;
221 ///
222 /// println!("Read {} bytes", n);
223 ///
224 /// // Close the file
225 /// f.close().await?;
226 /// Ok(())
227 /// })
228 /// }
229 /// ```
230 pub async fn readv_at<T: BoundedBufMut>(
231 &self,
232 bufs: Vec<T>,
233 pos: u64,
234 ) -> crate::BufResult<usize, Vec<T>> {
235 // Submit the read operation
236 let op = Op::readv_at(&self.fd, bufs, pos).unwrap();
237 op.await
238 }
239
240 /// Write data from buffers into this file at the specified offset,
241 /// returning how many bytes were written.
242 ///
243 /// This function will attempt to write the entire contents of `bufs`, but
244 /// the entire write may not succeed, or the write may also generate an
245 /// error. The bytes will be written starting at the specified offset.
246 ///
247 /// # Return
248 ///
249 /// The method returns the operation result and the same array of buffers passed
250 /// in as an argument. A return value of `0` typically means that the
251 /// underlying file is no longer able to accept bytes and will likely not be
252 /// able to in the future as well, or that the buffer provided is empty.
253 ///
254 /// # Errors
255 ///
256 /// Each call to `write` may generate an I/O error indicating that the
257 /// operation could not be completed. If an error is returned then no bytes
258 /// in the buffer were written to this writer.
259 ///
260 /// It is **not** considered an error if the entire buffer could not be
261 /// written to this writer.
262 ///
263 /// # Examples
264 ///
265 /// ```no_run
266 /// use tokio_uring::fs::File;
267 ///
268 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
269 /// tokio_uring::start(async {
270 /// let file = File::create("foo.txt").await?;
271 ///
272 /// // Writes some prefix of the byte string, not necessarily all of it.
273 /// let bufs = vec!["some".to_owned().into_bytes(), " bytes".to_owned().into_bytes()];
274 /// let (res, _) = file.writev_at(bufs, 0).await;
275 /// let n = res?;
276 ///
277 /// println!("wrote {} bytes", n);
278 ///
279 /// // Close the file
280 /// file.close().await?;
281 /// Ok(())
282 /// })
283 /// }
284 /// ```
285 ///
286 /// [`Ok(n)`]: Ok
287 pub async fn writev_at<T: BoundedBuf>(
288 &self,
289 buf: Vec<T>,
290 pos: u64,
291 ) -> crate::BufResult<usize, Vec<T>> {
292 let op = Op::writev_at(&self.fd, buf, pos).unwrap();
293 op.await
294 }
295
296 /// Like `writev_at` but will call the `io_uring` `writev` operation multiple times if
297 /// necessary.
298 ///
299 /// Parameter `pos` is an `Option<u64>` to allow this function to be used for both files that
300 /// are seekable and those that are not. The caller is responsible for knowing this.
301 ///
302 /// When `None` is supplied, the offset passed to the `io_uring` call will always be zero, even
303 /// if multiple writev calls are necessary; only the iovec information would be adjusted
304 /// between calls. A Unix pipe would fall into this category.
305 ///
306 /// When `Some(n)` is suppied, the offset passed to the writev call will be incremented by the
307 /// progress of prior writev calls. A file system's regular file would fall into this category.
308 ///
309 /// If the caller passes `Some(n)` for a file that is not seekable, the `io_uring` `writev`
310 /// operation will return an error once n is not zero.
311 ///
312 /// If the caller passes `None`, when the file *is* seekable, when multiple `writev` calls are
313 /// required to complete the writing of all the bytes, the bytes at position 0 of the file will
314 /// have been overwritten one or more times with incorrect data. This is true just as if the
315 /// caller had invoked seperate write calls to a file, all with position 0, when in fact the
316 /// file was seekable.
317 ///
318 /// Performance considerations:
319 ///
320 /// The user may want to check that this function is necessary in their use case or performs
321 /// better than a series of write_all operations would. There is overhead either way and it is
322 /// not clear which should be faster or give better throughput.
323 ///
324 /// This function causes the temporary allocation of a Vec one time to hold the array of iovec
325 /// that is passed to the kernel. The same array is used for any subsequent calls to get all
326 /// the bytes written. Whereas individual calls to write_all do not require the Vec to be
327 /// allocated, they do each incur the normal overhead of setting up the submission and
328 /// completion structures and going through the future poll mechanism.
329 ///
330 /// TODO decide, would a separate `writev_all` function for `file` that did not take a `pos`
331 /// make things less ambiguous?
332 ///
333 /// TODO more complete documentation here.
334 /// TODO define writev_all functions for net/unix/stream, net/tcp/stream, io/socket.
335 /// TODO remove usize from result, to be consistent with other write_all_vectored functions.
336 /// TODO find a way to test this with some stress to the file so the writev calls don't all
337 /// succeed on their first try.
338 /// TODO consider replacing the current `write_all` and `write_all_at` functions with a similar
339 /// mechanism so all the write-all logic is in one place, in the io/write_all.rs file.
340 pub async fn writev_at_all<T: BoundedBuf>(
341 &self,
342 buf: Vec<T>,
343 pos: Option<u64>, // Use None for files that can't seek
344 ) -> crate::BufResult<usize, Vec<T>> {
345 let op = crate::io::writev_at_all(&self.fd, buf, pos);
346 op.await
347 }
348
349 /// Read the exact number of bytes required to fill `buf` at the specified
350 /// offset from the file.
351 ///
352 /// This function reads as many as bytes as necessary to completely fill the
353 /// specified buffer `buf`.
354 ///
355 /// # Return
356 ///
357 /// The method returns the operation result and the same buffer value passed
358 /// as an argument.
359 ///
360 /// If the method returns [`Ok(())`], then the read was successful.
361 ///
362 /// # Errors
363 ///
364 /// If this function encounters an "end of file" before completely filling
365 /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
366 /// The buffer is returned on error.
367 ///
368 /// If this function encounters any form of I/O or other error, an error
369 /// variant will be returned. The buffer is returned on error.
370 ///
371 /// # Examples
372 ///
373 /// ```no_run
374 /// use tokio_uring::fs::File;
375 ///
376 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
377 /// tokio_uring::start(async {
378 /// let f = File::open("foo.txt").await?;
379 /// let buffer = Vec::with_capacity(10);
380 ///
381 /// // Read up to 10 bytes
382 /// let (res, buffer) = f.read_exact_at(buffer, 0).await;
383 /// res?;
384 ///
385 /// println!("The bytes: {:?}", buffer);
386 ///
387 /// // Close the file
388 /// f.close().await?;
389 /// Ok(())
390 /// })
391 /// }
392 /// ```
393 ///
394 /// [`ErrorKind::UnexpectedEof`]: std::io::ErrorKind::UnexpectedEof
395 pub async fn read_exact_at<T>(&self, buf: T, pos: u64) -> crate::BufResult<(), T>
396 where
397 T: BoundedBufMut,
398 {
399 let orig_bounds = buf.bounds();
400 let (res, buf) = self.read_exact_slice_at(buf.slice_full(), pos).await;
401 (res, T::from_buf_bounds(buf, orig_bounds))
402 }
403
404 async fn read_exact_slice_at<T: IoBufMut>(
405 &self,
406 mut buf: Slice<T>,
407 mut pos: u64,
408 ) -> crate::BufResult<(), T> {
409 if pos.checked_add(buf.bytes_total() as u64).is_none() {
410 return (
411 Err(io::Error::new(
412 io::ErrorKind::InvalidInput,
413 "buffer too large for file",
414 )),
415 buf.into_inner(),
416 );
417 }
418
419 while buf.bytes_total() != 0 {
420 let (res, slice) = self.read_at(buf, pos).await;
421 match res {
422 Ok(0) => {
423 return (
424 Err(io::Error::new(
425 io::ErrorKind::UnexpectedEof,
426 "failed to fill whole buffer",
427 )),
428 slice.into_inner(),
429 )
430 }
431 Ok(n) => {
432 pos += n as u64;
433 buf = slice.slice(n..);
434 }
435
436 // No match on an EINTR error is performed because this
437 // crate's design ensures we are not calling the 'wait' option
438 // in the ENTER syscall. Only an Enter with 'wait' can generate
439 // an EINTR according to the io_uring man pages.
440 Err(e) => return (Err(e), slice.into_inner()),
441 };
442 }
443
444 (Ok(()), buf.into_inner())
445 }
446
447 /// Like [`read_at`], but using a pre-mapped buffer
448 /// registered with [`FixedBufRegistry`].
449 ///
450 /// [`read_at`]: Self::read_at
451 /// [`FixedBufRegistry`]: crate::buf::fixed::FixedBufRegistry
452 ///
453 /// # Errors
454 ///
455 /// In addition to errors that can be reported by `read_at`,
456 /// this operation fails if the buffer is not registered in the
457 /// current `tokio-uring` runtime.
458 ///
459 /// # Examples
460 ///
461 /// ```no_run
462 ///# fn main() -> Result<(), Box<dyn std::error::Error>> {
463 /// use tokio_uring::fs::File;
464 /// use tokio_uring::buf::fixed::FixedBufRegistry;
465 /// use tokio_uring::buf::BoundedBuf;
466 /// use std::iter;
467 ///
468 /// tokio_uring::start(async {
469 /// let registry = FixedBufRegistry::new(iter::repeat(vec![0; 10]).take(10));
470 /// registry.register()?;
471 ///
472 /// let f = File::open("foo.txt").await?;
473 /// let buffer = registry.check_out(2).unwrap();
474 ///
475 /// // Read up to 10 bytes
476 /// let (res, buffer) = f.read_fixed_at(buffer, 0).await;
477 /// let n = res?;
478 ///
479 /// println!("The bytes: {:?}", &buffer[..n]);
480 ///
481 /// // Close the file
482 /// f.close().await?;
483 /// Ok(())
484 /// })
485 ///# }
486 /// ```
487 pub async fn read_fixed_at<T>(&self, buf: T, pos: u64) -> crate::BufResult<usize, T>
488 where
489 T: BoundedBufMut<BufMut = FixedBuf>,
490 {
491 // Submit the read operation
492 let op = Op::read_fixed_at(&self.fd, buf, pos).unwrap();
493 op.await
494 }
495
496 /// Write a buffer into this file at the specified offset, returning how
497 /// many bytes were written.
498 ///
499 /// This function will attempt to write the entire contents of `buf`, but
500 /// the entire write may not succeed, or the write may also generate an
501 /// error. The bytes will be written starting at the specified offset.
502 ///
503 /// # Return
504 ///
505 /// The method returns the operation result and the same buffer value passed
506 /// in as an argument. A return value of `0` typically means that the
507 /// underlying file is no longer able to accept bytes and will likely not be
508 /// able to in the future as well, or that the buffer provided is empty.
509 ///
510 /// # Errors
511 ///
512 /// Each call to `write` may generate an I/O error indicating that the
513 /// operation could not be completed. If an error is returned then no bytes
514 /// in the buffer were written to this writer.
515 ///
516 /// It is **not** considered an error if the entire buffer could not be
517 /// written to this writer.
518 ///
519 /// # Examples
520 ///
521 /// ```no_run
522 /// use tokio_uring::fs::File;
523 ///
524 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
525 /// tokio_uring::start(async {
526 /// let file = File::create("foo.txt").await?;
527 ///
528 /// // Writes some prefix of the byte string, not necessarily all of it.
529 /// let (res, _) = file.write_at(&b"some bytes"[..], 0).submit().await;
530 /// let n = res?;
531 ///
532 /// println!("wrote {} bytes", n);
533 ///
534 /// // Close the file
535 /// file.close().await?;
536 /// Ok(())
537 /// })
538 /// }
539 /// ```
540 ///
541 /// [`Ok(n)`]: Ok
542 pub fn write_at<T: BoundedBuf>(&self, buf: T, pos: u64) -> UnsubmittedWrite<T> {
543 UnsubmittedOneshot::write_at(&self.fd, buf, pos)
544 }
545
546 /// Attempts to write an entire buffer into this file at the specified offset.
547 ///
548 /// This method will continuously call [`write_at`] until there is no more data
549 /// to be written or an error is returned.
550 /// This method will not return until the entire buffer has been successfully
551 /// written or an error occurs.
552 ///
553 /// If the buffer contains no data, this will never call [`write_at`].
554 ///
555 /// # Return
556 ///
557 /// The method returns the operation result and the same buffer value passed
558 /// in as an argument.
559 ///
560 /// # Errors
561 ///
562 /// This function will return the first error that [`write_at`] returns.
563 ///
564 /// # Examples
565 ///
566 /// ```no_run
567 /// use tokio_uring::fs::File;
568 ///
569 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
570 /// tokio_uring::start(async {
571 /// let file = File::create("foo.txt").await?;
572 ///
573 /// // Writes some prefix of the byte string, not necessarily all of it.
574 /// let (res, _) = file.write_all_at(&b"some bytes"[..], 0).await;
575 /// res?;
576 ///
577 /// println!("wrote all bytes");
578 ///
579 /// // Close the file
580 /// file.close().await?;
581 /// Ok(())
582 /// })
583 /// }
584 /// ```
585 ///
586 /// [`write_at`]: File::write_at
587 pub async fn write_all_at<T>(&self, buf: T, pos: u64) -> crate::BufResult<(), T>
588 where
589 T: BoundedBuf,
590 {
591 let orig_bounds = buf.bounds();
592 let (res, buf) = self.write_all_slice_at(buf.slice_full(), pos).await;
593 (res, T::from_buf_bounds(buf, orig_bounds))
594 }
595
596 async fn write_all_slice_at<T: IoBuf>(
597 &self,
598 mut buf: Slice<T>,
599 mut pos: u64,
600 ) -> crate::BufResult<(), T> {
601 if pos.checked_add(buf.bytes_init() as u64).is_none() {
602 return (
603 Err(io::Error::new(
604 io::ErrorKind::InvalidInput,
605 "buffer too large for file",
606 )),
607 buf.into_inner(),
608 );
609 }
610
611 while buf.bytes_init() != 0 {
612 let (res, slice) = self.write_at(buf, pos).submit().await;
613 match res {
614 Ok(0) => {
615 return (
616 Err(io::Error::new(
617 io::ErrorKind::WriteZero,
618 "failed to write whole buffer",
619 )),
620 slice.into_inner(),
621 )
622 }
623 Ok(n) => {
624 pos += n as u64;
625 buf = slice.slice(n..);
626 }
627
628 // No match on an EINTR error is performed because this
629 // crate's design ensures we are not calling the 'wait' option
630 // in the ENTER syscall. Only an Enter with 'wait' can generate
631 // an EINTR according to the io_uring man pages.
632 Err(e) => return (Err(e), slice.into_inner()),
633 };
634 }
635
636 (Ok(()), buf.into_inner())
637 }
638
639 /// Like [`write_at`], but using a pre-mapped buffer
640 /// registered with [`FixedBufRegistry`].
641 ///
642 /// [`write_at`]: Self::write_at
643 /// [`FixedBufRegistry`]: crate::buf::fixed::FixedBufRegistry
644 ///
645 /// # Errors
646 ///
647 /// In addition to errors that can be reported by `write_at`,
648 /// this operation fails if the buffer is not registered in the
649 /// current `tokio-uring` runtime.
650 ///
651 /// # Examples
652 ///
653 /// ```no_run
654 ///# fn main() -> Result<(), Box<dyn std::error::Error>> {
655 /// use tokio_uring::fs::File;
656 /// use tokio_uring::buf::fixed::FixedBufRegistry;
657 /// use tokio_uring::buf::BoundedBuf;
658 ///
659 /// tokio_uring::start(async {
660 /// let registry = FixedBufRegistry::new([b"some bytes".to_vec()]);
661 /// registry.register()?;
662 ///
663 /// let file = File::create("foo.txt").await?;
664 ///
665 /// let buffer = registry.check_out(0).unwrap();
666 ///
667 /// // Writes some prefix of the buffer content,
668 /// // not necessarily all of it.
669 /// let (res, _) = file.write_fixed_at(buffer, 0).await;
670 /// let n = res?;
671 ///
672 /// println!("wrote {} bytes", n);
673 ///
674 /// // Close the file
675 /// file.close().await?;
676 /// Ok(())
677 /// })
678 ///# }
679 /// ```
680 pub async fn write_fixed_at<T>(&self, buf: T, pos: u64) -> crate::BufResult<usize, T>
681 where
682 T: BoundedBuf<Buf = FixedBuf>,
683 {
684 let op = Op::write_fixed_at(&self.fd, buf, pos).unwrap();
685 op.await
686 }
687
688 /// Attempts to write an entire buffer into this file at the specified offset.
689 ///
690 /// This method will continuously call [`write_fixed_at`] until there is no more data
691 /// to be written or an error is returned.
692 /// This method will not return until the entire buffer has been successfully
693 /// written or an error occurs.
694 ///
695 /// If the buffer contains no data, this will never call [`write_fixed_at`].
696 ///
697 /// # Return
698 ///
699 /// The method returns the operation result and the same buffer value passed
700 /// in as an argument.
701 ///
702 /// # Errors
703 ///
704 /// This function will return the first error that [`write_fixed_at`] returns.
705 ///
706 /// [`write_fixed_at`]: Self::write_fixed_at
707 pub async fn write_fixed_all_at<T>(&self, buf: T, pos: u64) -> crate::BufResult<(), T>
708 where
709 T: BoundedBuf<Buf = FixedBuf>,
710 {
711 let orig_bounds = buf.bounds();
712 let (res, buf) = self.write_fixed_all_at_slice(buf.slice_full(), pos).await;
713 (res, T::from_buf_bounds(buf, orig_bounds))
714 }
715
716 async fn write_fixed_all_at_slice(
717 &self,
718 mut buf: Slice<FixedBuf>,
719 mut pos: u64,
720 ) -> crate::BufResult<(), FixedBuf> {
721 if pos.checked_add(buf.bytes_init() as u64).is_none() {
722 return (
723 Err(io::Error::new(
724 io::ErrorKind::InvalidInput,
725 "buffer too large for file",
726 )),
727 buf.into_inner(),
728 );
729 }
730
731 while buf.bytes_init() != 0 {
732 let (res, slice) = self.write_fixed_at(buf, pos).await;
733 match res {
734 Ok(0) => {
735 return (
736 Err(io::Error::new(
737 io::ErrorKind::WriteZero,
738 "failed to write whole buffer",
739 )),
740 slice.into_inner(),
741 )
742 }
743 Ok(n) => {
744 pos += n as u64;
745 buf = slice.slice(n..);
746 }
747
748 // No match on an EINTR error is performed because this
749 // crate's design ensures we are not calling the 'wait' option
750 // in the ENTER syscall. Only an Enter with 'wait' can generate
751 // an EINTR according to the io_uring man pages.
752 Err(e) => return (Err(e), slice.into_inner()),
753 };
754 }
755
756 (Ok(()), buf.into_inner())
757 }
758
759 /// Attempts to sync all OS-internal metadata to disk.
760 ///
761 /// This function will attempt to ensure that all in-memory data reaches the
762 /// filesystem before completing.
763 ///
764 /// This can be used to handle errors that would otherwise only be caught
765 /// when the `File` is closed. Dropping a file will ignore errors in
766 /// synchronizing this in-memory data.
767 ///
768 /// # Examples
769 ///
770 /// ```no_run
771 /// use tokio_uring::fs::File;
772 ///
773 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
774 /// tokio_uring::start(async {
775 /// let f = File::create("foo.txt").await?;
776 /// let (res, buf) = f.write_at(&b"Hello, world!"[..], 0).submit().await;
777 /// let n = res?;
778 ///
779 /// f.sync_all().await?;
780 ///
781 /// // Close the file
782 /// f.close().await?;
783 /// Ok(())
784 /// })
785 /// }
786 /// ```
787 pub async fn sync_all(&self) -> io::Result<()> {
788 Op::fsync(&self.fd)?.await
789 }
790
791 /// Attempts to sync file data to disk.
792 ///
793 /// This method is similar to [`sync_all`], except that it may not
794 /// synchronize file metadata to the filesystem.
795 ///
796 /// This is intended for use cases that must synchronize content, but don't
797 /// need the metadata on disk. The goal of this method is to reduce disk
798 /// operations.
799 ///
800 /// Note that some platforms may simply implement this in terms of
801 /// [`sync_all`].
802 ///
803 /// [`sync_all`]: File::sync_all
804 ///
805 /// # Examples
806 ///
807 /// ```no_run
808 /// use tokio_uring::fs::File;
809 ///
810 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
811 /// tokio_uring::start(async {
812 /// let f = File::create("foo.txt").await?;
813 /// let (res, buf) = f.write_at(&b"Hello, world!"[..], 0).submit().await;
814 /// let n = res?;
815 ///
816 /// f.sync_data().await?;
817 ///
818 /// // Close the file
819 /// f.close().await?;
820 /// Ok(())
821 /// })
822 /// }
823 /// ```
824 pub async fn sync_data(&self) -> io::Result<()> {
825 Op::datasync(&self.fd)?.await
826 }
827
828 /// Manipulate the allocated disk space of the file.
829 ///
830 /// The manipulated range starts at the `offset` and continues for `len` bytes.
831 ///
832 /// The specific manipulation to the allocated disk space are specified by
833 /// the `flags`, to understand what are the possible values here check
834 /// the `fallocate(2)` man page.
835 ///
836 /// # Examples
837 ///
838 /// ```no_run
839 /// use tokio_uring::fs::File;
840 ///
841 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
842 /// tokio_uring::start(async {
843 /// let f = File::create("foo.txt").await?;
844 ///
845 /// // Allocate a 1024 byte file setting all the bytes to zero
846 /// f.fallocate(0, 1024, libc::FALLOC_FL_ZERO_RANGE).await?;
847 ///
848 /// // Close the file
849 /// f.close().await?;
850 /// Ok(())
851 /// })
852 /// }
853 pub async fn fallocate(&self, offset: u64, len: u64, flags: i32) -> io::Result<()> {
854 Op::fallocate(&self.fd, offset, len, flags)?.await
855 }
856
857 /// Closes the file using the uring asynchronous close operation and returns the possible error
858 /// as described in the close(2) man page.
859 ///
860 /// The programmer has the choice of calling this asynchronous close and waiting for the result
861 /// or letting the library close the file automatically and simply letting the file go out of
862 /// scope and having the library close the file descriptor automatically and synchronously.
863 ///
864 /// Calling this asynchronous close is to be preferred because it returns the close result
865 /// which as the man page points out, should not be ignored. This asynchronous close also
866 /// avoids the synchronous close system call and may result in better throughput as the thread
867 /// is not blocked during the close.
868 ///
869 /// # Examples
870 ///
871 /// ```no_run
872 /// use tokio_uring::fs::File;
873 ///
874 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
875 /// tokio_uring::start(async {
876 /// // Open the file
877 /// let f = File::open("foo.txt").await?;
878 /// // Close the file
879 /// f.close().await?;
880 ///
881 /// Ok(())
882 /// })
883 /// }
884 /// ```
885 pub async fn close(mut self) -> io::Result<()> {
886 self.fd.close().await
887 }
888}
889
890impl FromRawFd for File {
891 unsafe fn from_raw_fd(fd: RawFd) -> Self {
892 File::from_shared_fd(SharedFd::new(fd))
893 }
894}
895
896impl AsRawFd for File {
897 fn as_raw_fd(&self) -> RawFd {
898 self.fd.raw_fd()
899 }
900}
901
902impl fmt::Debug for File {
903 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
904 f.debug_struct("File")
905 .field("fd", &self.fd.raw_fd())
906 .finish()
907 }
908}
909
910/// Removes a File
911///
912/// This function will return an error in the following situations, but is not
913/// limited to just these cases:
914///
915/// * `path` doesn't exist.
916/// * [`io::ErrorKind`] would be set to `NotFound`
917/// * The user lacks permissions to modify/remove the file at the provided `path`.
918/// * [`io::ErrorKind`] would be set to `PermissionDenied`
919///
920/// # Examples
921///
922/// ```no_run
923/// use tokio_uring::fs::remove_file;
924///
925/// fn main() -> Result<(), Box<dyn std::error::Error>> {
926/// tokio_uring::start(async {
927/// remove_file("/some/file.txt").await?;
928/// Ok::<(), std::io::Error>(())
929/// })?;
930/// Ok(())
931/// }
932/// ```
933pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
934 Op::unlink_file(path.as_ref())?.await
935}
936
937/// Renames a file or directory to a new name, replacing the original file if
938/// `to` already exists.
939///
940/// #Errors
941///
942/// * `path` doesn't exist.
943/// * [`io::ErrorKind`] would be set to `NotFound`
944/// * The user lacks permissions to modify/remove the file at the provided `path`.
945/// * [`io::ErrorKind`] would be set to `PermissionDenied`
946/// * The new name/path is on a different mount point.
947/// * [`io::ErrorKind`] would be set to `CrossesDevices`
948///
949/// # Example
950///
951/// ```no_run
952/// use tokio_uring::fs::rename;
953///
954/// fn main() -> Result<(), Box<dyn std::error::Error>> {
955/// tokio_uring::start(async {
956/// rename("a.txt", "b.txt").await?; // Rename a.txt to b.txt
957/// Ok::<(), std::io::Error>(())
958/// })?;
959/// Ok(())
960/// }
961/// ```
962pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
963 Op::rename_at(from.as_ref(), to.as_ref(), 0)?.await
964}