1 //! Types for working with [`File`].
2 //!
3 //! [`File`]: file/struct.File.html
4 
5 mod create;
6 mod metadata;
7 mod open;
8 mod open_options;
9 mod seek;
10 
11 pub use self::create::CreateFuture;
12 pub use self::metadata::MetadataFuture;
13 pub use self::open::OpenFuture;
14 pub use self::open_options::OpenOptions;
15 pub use self::seek::SeekFuture;
16 
17 use tokio_io::{AsyncRead, AsyncWrite};
18 
19 use futures::Poll;
20 
21 use std::fs::{File as StdFile, Metadata, Permissions};
22 use std::io::{self, Read, Write, Seek};
23 use std::path::Path;
24 
25 /// A reference to an open file on the filesystem.
26 ///
27 /// This is a specialized version of [`std::fs::File`][std] for usage from the
28 /// Tokio runtime.
29 ///
30 /// An instance of a `File` can be read and/or written depending on what options
31 /// it was opened with. Files also implement Seek to alter the logical cursor
32 /// that the file contains internally.
33 ///
34 /// Files are automatically closed when they go out of scope.
35 ///
36 /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
37 #[derive(Debug)]
38 pub struct File {
39     std: Option<StdFile>,
40 }
41 
42 impl File {
43     /// Attempts to open a file in read-only mode.
44     ///
45     /// See [`OpenOptions`] for more details.
46     ///
47     /// [`OpenOptions`]: struct.OpenOptions.html
48     ///
49     /// # Errors
50     ///
51     /// `OpenFuture` results in an error if called from outside of the Tokio
52     /// runtime or if the underlying [`open`] call results in an error.
53     ///
54     /// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
open<P>(path: P) -> OpenFuture<P> where P: AsRef<Path> + Send + 'static,55     pub fn open<P>(path: P) -> OpenFuture<P>
56     where P: AsRef<Path> + Send + 'static,
57     {
58         OpenOptions::new().read(true).open(path)
59     }
60 
61     /// Opens a file in write-only mode.
62     ///
63     /// This function will create a file if it does not exist, and will truncate
64     /// it if it does.
65     ///
66     /// See [`OpenOptions`] for more details.
67     ///
68     /// [`OpenOptions`]: struct.OpenOptions.html
69     ///
70     /// # Errors
71     ///
72     /// `CreateFuture` results in an error if called from outside of the Tokio
73     /// runtime or if the underlying [`create`] call results in an error.
74     ///
75     /// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
create<P>(path: P) -> CreateFuture<P> where P: AsRef<Path> + Send + 'static,76     pub fn create<P>(path: P) -> CreateFuture<P>
77     where P: AsRef<Path> + Send + 'static,
78     {
79         CreateFuture::new(path)
80     }
81 
82     /// Convert a [`std::fs::File`][std] to a `tokio_fs::File`.
83     ///
84     /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
from_std(std: StdFile) -> File85     pub(crate) fn from_std(std: StdFile) -> File {
86         File { std: Some(std) }
87     }
88 
89     /// Seek to an offset, in bytes, in a stream.
90     ///
91     /// A seek beyond the end of a stream is allowed, but implementation
92     /// defined.
93     ///
94     /// If the seek operation completed successfully, this method returns the
95     /// new position from the start of the stream. That position can be used
96     /// later with `SeekFrom::Start`.
97     ///
98     /// # Errors
99     ///
100     /// Seeking to a negative offset is considered an error.
poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error>101     pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error> {
102         ::blocking_io(|| self.std().seek(pos))
103     }
104 
105     /// Seek to an offset, in bytes, in a stream.
106     ///
107     /// Similar to `poll_seek`, but returning a `Future`.
108     ///
109     /// This method consumes the `File` and returns it back when the future
110     /// completes.
seek(self, pos: io::SeekFrom) -> SeekFuture111     pub fn seek(self, pos: io::SeekFrom) -> SeekFuture {
112         SeekFuture::new(self, pos)
113     }
114 
115     /// Attempts to sync all OS-internal metadata to disk.
116     ///
117     /// This function will attempt to ensure that all in-core data reaches the
118     /// filesystem before returning.
poll_sync_all(&mut self) -> Poll<(), io::Error>119     pub fn poll_sync_all(&mut self) -> Poll<(), io::Error> {
120         ::blocking_io(|| self.std().sync_all())
121     }
122 
123     /// This function is similar to `poll_sync_all`, except that it may not
124     /// synchronize file metadata to the filesystem.
125     ///
126     /// This is intended for use cases that must synchronize content, but don't
127     /// need the metadata on disk. The goal of this method is to reduce disk
128     /// operations.
129     ///
130     /// Note that some platforms may simply implement this in terms of `poll_sync_all`.
poll_sync_data(&mut self) -> Poll<(), io::Error>131     pub fn poll_sync_data(&mut self) -> Poll<(), io::Error> {
132         ::blocking_io(|| self.std().sync_data())
133     }
134 
135     /// Truncates or extends the underlying file, updating the size of this file to become size.
136     ///
137     /// If the size is less than the current file's size, then the file will be
138     /// shrunk. If it is greater than the current file's size, then the file
139     /// will be extended to size and have all of the intermediate data filled in
140     /// with 0s.
141     ///
142     /// # Errors
143     ///
144     /// This function will return an error if the file is not opened for
145     /// writing.
poll_set_len(&mut self, size: u64) -> Poll<(), io::Error>146     pub fn poll_set_len(&mut self, size: u64) -> Poll<(), io::Error> {
147         ::blocking_io(|| self.std().set_len(size))
148     }
149 
150     /// Queries metadata about the underlying file.
metadata(self) -> MetadataFuture151     pub fn metadata(self) -> MetadataFuture {
152         MetadataFuture::new(self)
153     }
154 
155     /// Queries metadata about the underlying file.
poll_metadata(&mut self) -> Poll<Metadata, io::Error>156     pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
157         ::blocking_io(|| self.std().metadata())
158     }
159 
160     /// Create a new `File` instance that shares the same underlying file handle
161     /// as the existing `File` instance. Reads, writes, and seeks will affect both
162     /// File instances simultaneously.
poll_try_clone(&mut self) -> Poll<File, io::Error>163     pub fn poll_try_clone(&mut self) -> Poll<File, io::Error> {
164         ::blocking_io(|| {
165             let std = self.std().try_clone()?;
166             Ok(File::from_std(std))
167         })
168     }
169 
170     /// Changes the permissions on the underlying file.
171     ///
172     /// # Platform-specific behavior
173     ///
174     /// This function currently corresponds to the `fchmod` function on Unix and
175     /// the `SetFileInformationByHandle` function on Windows. Note that, this
176     /// [may change in the future][changes].
177     ///
178     /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
179     ///
180     /// # Errors
181     ///
182     /// This function will return an error if the user lacks permission change
183     /// attributes on the underlying file. It may also return an error in other
184     /// os-specific unspecified cases.
poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error>185     pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error> {
186         ::blocking_io(|| self.std().set_permissions(perm))
187     }
188 
189     /// Destructures the `tokio_fs::File` into a [`std::fs::File`][std].
190     ///
191     /// # Panics
192     ///
193     /// This function will panic if `shutdown` has been called.
194     ///
195     /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
into_std(mut self) -> StdFile196     pub fn into_std(mut self) -> StdFile {
197         self.std.take().expect("`File` instance already shutdown")
198     }
199 
std(&mut self) -> &mut StdFile200     fn std(&mut self) -> &mut StdFile {
201         self.std.as_mut().expect("`File` instance already shutdown")
202     }
203 }
204 
205 impl Read for File {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>206     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
207         ::would_block(|| self.std().read(buf))
208     }
209 }
210 
211 impl AsyncRead for File {
prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool212     unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
213         false
214     }
215 }
216 
217 impl Write for File {
write(&mut self, buf: &[u8]) -> io::Result<usize>218     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
219         ::would_block(|| self.std().write(buf))
220     }
221 
flush(&mut self) -> io::Result<()>222     fn flush(&mut self) -> io::Result<()> {
223         ::would_block(|| self.std().flush())
224     }
225 }
226 
227 impl AsyncWrite for File {
shutdown(&mut self) -> Poll<(), io::Error>228     fn shutdown(&mut self) -> Poll<(), io::Error> {
229         ::blocking_io(|| {
230             self.std = None;
231             Ok(())
232         })
233     }
234 }
235 
236 impl Drop for File {
drop(&mut self)237     fn drop(&mut self) {
238         if let Some(_std) = self.std.take() {
239             // This is probably fine as closing a file *shouldn't* be a blocking
240             // operation. That said, ideally `shutdown` is called first.
241         }
242     }
243 }
244