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}