1 //! Types for working with [`File`].
2 //!
3 //! [`File`]: file/struct.File.html
4 
5 mod clone;
6 mod create;
7 mod metadata;
8 mod open;
9 mod open_options;
10 mod seek;
11 
12 pub use self::clone::CloneFuture;
13 pub use self::create::CreateFuture;
14 pub use self::metadata::MetadataFuture;
15 pub use self::open::OpenFuture;
16 pub use self::open_options::OpenOptions;
17 pub use self::seek::SeekFuture;
18 
19 use tokio_io::{AsyncRead, AsyncWrite};
20 
21 use futures::Poll;
22 
23 use std::fs::{File as StdFile, Metadata, Permissions};
24 use std::io::{self, Read, Seek, Write};
25 use std::path::Path;
26 
27 /// A reference to an open file on the filesystem.
28 ///
29 /// This is a specialized version of [`std::fs::File`][std] for usage from the
30 /// Tokio runtime.
31 ///
32 /// An instance of a `File` can be read and/or written depending on what options
33 /// it was opened with. Files also implement Seek to alter the logical cursor
34 /// that the file contains internally.
35 ///
36 /// Files are automatically closed when they go out of scope.
37 ///
38 /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
39 ///
40 /// # Examples
41 ///
42 /// Create a new file and asynchronously write bytes to it:
43 ///
44 /// ```no_run
45 /// extern crate tokio;
46 ///
47 /// use tokio::prelude::{AsyncWrite, Future};
48 ///
49 /// fn main() {
50 ///     let task = tokio::fs::File::create("foo.txt")
51 ///         .and_then(|mut file| file.poll_write(b"hello, world!"))
52 ///         .map(|res| {
53 ///             println!("{:?}", res);
54 ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
55 ///
56 ///     tokio::run(task);
57 /// }
58 /// ```
59 ///
60 /// Read the contents of a file into a buffer
61 ///
62 /// ```no_run
63 /// extern crate tokio;
64 ///
65 /// use tokio::prelude::{AsyncRead, Future};
66 ///
67 /// fn main() {
68 ///     let task = tokio::fs::File::open("foo.txt")
69 ///         .and_then(|mut file| {
70 ///             let mut contents = vec![];
71 ///             file.read_buf(&mut contents)
72 ///                 .map(|res| {
73 ///                     println!("{:?}", res);
74 ///                 })
75 ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
76 ///     tokio::run(task);
77 /// }
78 /// ```
79 #[derive(Debug)]
80 pub struct File {
81     std: Option<StdFile>,
82 }
83 
84 impl File {
85     /// Attempts to open a file in read-only mode.
86     ///
87     /// See [`OpenOptions`] for more details.
88     ///
89     /// [`OpenOptions`]: struct.OpenOptions.html
90     ///
91     /// # Errors
92     ///
93     /// `OpenFuture` results in an error if called from outside of the Tokio
94     /// runtime or if the underlying [`open`] call results in an error.
95     ///
96     /// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
97     ///
98     /// # Examples
99     ///
100     /// ```no_run
101     /// # extern crate tokio;
102     /// use tokio::prelude::Future;
103     /// fn main() {
104     ///     let task = tokio::fs::File::open("foo.txt").and_then(|file| {
105     ///         // do something with the file ...
106     ///         file.metadata().map(|md| println!("{:?}", md))
107     ///     }).map_err(|e| {
108     ///         // handle errors
109     ///         eprintln!("IO error: {:?}", e);
110     ///     });
111     ///     tokio::run(task);
112     /// }
113     /// ```
open<P>(path: P) -> OpenFuture<P> where P: AsRef<Path> + Send + 'static,114     pub fn open<P>(path: P) -> OpenFuture<P>
115     where
116         P: AsRef<Path> + Send + 'static,
117     {
118         OpenOptions::new().read(true).open(path)
119     }
120 
121     /// Opens a file in write-only mode.
122     ///
123     /// This function will create a file if it does not exist, and will truncate
124     /// it if it does.
125     ///
126     /// See [`OpenOptions`] for more details.
127     ///
128     /// [`OpenOptions`]: struct.OpenOptions.html
129     ///
130     /// # Errors
131     ///
132     /// `CreateFuture` results in an error if called from outside of the Tokio
133     /// runtime or if the underlying [`create`] call results in an error.
134     ///
135     /// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
136     ///
137     /// # Examples
138     ///
139     /// ```no_run
140     /// # extern crate tokio;
141     /// use tokio::prelude::Future;
142     /// fn main() {
143     ///     let task = tokio::fs::File::create("foo.txt")
144     ///         .and_then(|file| {
145     ///             // do something with the created file ...
146     ///             file.metadata().map(|md| println!("{:?}", md))
147     ///         }).map_err(|e| {
148     ///             // handle errors
149     ///             eprintln!("IO error: {:?}", e);
150     ///     });
151     ///     tokio::run(task);
152     /// }
153     /// ```
create<P>(path: P) -> CreateFuture<P> where P: AsRef<Path> + Send + 'static,154     pub fn create<P>(path: P) -> CreateFuture<P>
155     where
156         P: AsRef<Path> + Send + 'static,
157     {
158         CreateFuture::new(path)
159     }
160 
161     /// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file].
162     ///
163     /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
164     /// [file]: struct.File.html
165     ///
166     /// Examples
167     /// ```no_run
168     /// # extern crate tokio;
169     /// use std::fs::File;
170     ///
171     /// fn main() {
172     ///     let std_file = File::open("foo.txt").unwrap();
173     ///     let file = tokio::fs::File::from_std(std_file);
174     /// }
175     /// ```
from_std(std: StdFile) -> File176     pub fn from_std(std: StdFile) -> File {
177         File { std: Some(std) }
178     }
179 
180     /// Seek to an offset, in bytes, in a stream.
181     ///
182     /// A seek beyond the end of a stream is allowed, but implementation
183     /// defined.
184     ///
185     /// If the seek operation completed successfully, this method returns the
186     /// new position from the start of the stream. That position can be used
187     /// later with `SeekFrom::Start`.
188     ///
189     /// # Errors
190     ///
191     /// Seeking to a negative offset is considered an error.
192     ///
193     /// # Examples
194     ///
195     /// ```no_run
196     /// # extern crate tokio;
197     /// use tokio::prelude::Future;
198     /// use std::io::SeekFrom;
199     ///
200     /// fn main() {
201     ///     let task = tokio::fs::File::open("foo.txt")
202     ///         // move cursor 6 bytes from the start of the file
203     ///         .and_then(|mut file| file.poll_seek(SeekFrom::Start(6)))
204     ///         .map(|res| {
205     ///             println!("{:?}", res);
206     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
207     ///
208     ///     tokio::run(task);
209     /// }
210     /// ```
poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error>211     pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error> {
212         ::blocking_io(|| self.std().seek(pos))
213     }
214 
215     /// Seek to an offset, in bytes, in a stream.
216     ///
217     /// Similar to `poll_seek`, but returning a `Future`.
218     ///
219     /// This method consumes the `File` and returns it back when the future
220     /// completes.
221     ///
222     /// # Examples
223     ///
224     /// ```no_run
225     /// # extern crate tokio;
226     /// use tokio::prelude::Future;
227     /// use std::io::SeekFrom;
228     ///
229     /// fn main() {
230     ///     let task = tokio::fs::File::create("foo.txt")
231     ///         .and_then(|file| file.seek(SeekFrom::Start(6)))
232     ///         .map(|file| {
233     ///             // handle returned file ..
234     ///             # println!("{:?}", file);
235     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
236     ///
237     ///     tokio::run(task);
238     /// }
239     /// ```
seek(self, pos: io::SeekFrom) -> SeekFuture240     pub fn seek(self, pos: io::SeekFrom) -> SeekFuture {
241         SeekFuture::new(self, pos)
242     }
243 
244     /// Attempts to sync all OS-internal metadata to disk.
245     ///
246     /// This function will attempt to ensure that all in-core data reaches the
247     /// filesystem before returning.
248     ///
249     /// # Examples
250     ///
251     /// ```no_run
252     /// # extern crate tokio;
253     /// use tokio::prelude::{AsyncWrite, Future};
254     ///
255     /// fn main() {
256     ///     let task = tokio::fs::File::create("foo.txt")
257     ///         .and_then(|mut file| {
258     ///             file.poll_write(b"hello, world!")?;
259     ///             file.poll_sync_all()
260     ///         })
261     ///         .map(|res| {
262     ///             // handle returned result ..
263     ///             # println!("{:?}", res);
264     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
265     ///
266     ///     tokio::run(task);
267     /// }
268     /// ```
poll_sync_all(&mut self) -> Poll<(), io::Error>269     pub fn poll_sync_all(&mut self) -> Poll<(), io::Error> {
270         ::blocking_io(|| self.std().sync_all())
271     }
272 
273     /// This function is similar to `poll_sync_all`, except that it may not
274     /// synchronize file metadata to the filesystem.
275     ///
276     /// This is intended for use cases that must synchronize content, but don't
277     /// need the metadata on disk. The goal of this method is to reduce disk
278     /// operations.
279     ///
280     /// Note that some platforms may simply implement this in terms of `poll_sync_all`.
281     ///
282     /// # Examples
283     ///
284     /// ```no_run
285     /// # extern crate tokio;
286     /// use tokio::prelude::{AsyncWrite, Future};
287     ///
288     /// fn main() {
289     ///     let task = tokio::fs::File::create("foo.txt")
290     ///         .and_then(|mut file| {
291     ///             file.poll_write(b"hello, world!")?;
292     ///             file.poll_sync_data()
293     ///         })
294     ///         .map(|res| {
295     ///             // handle returned result ..
296     ///             # println!("{:?}", res);
297     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
298     ///
299     ///     tokio::run(task);
300     /// }
301     /// ```
poll_sync_data(&mut self) -> Poll<(), io::Error>302     pub fn poll_sync_data(&mut self) -> Poll<(), io::Error> {
303         ::blocking_io(|| self.std().sync_data())
304     }
305 
306     /// Truncates or extends the underlying file, updating the size of this file to become size.
307     ///
308     /// If the size is less than the current file's size, then the file will be
309     /// shrunk. If it is greater than the current file's size, then the file
310     /// will be extended to size and have all of the intermediate data filled in
311     /// with 0s.
312     ///
313     /// # Errors
314     ///
315     /// This function will return an error if the file is not opened for
316     /// writing.
317     ///
318     /// # Examples
319     ///
320     /// ```no_run
321     /// # extern crate tokio;
322     /// use tokio::prelude::Future;
323     ///
324     /// fn main() {
325     ///     let task = tokio::fs::File::create("foo.txt")
326     ///         .and_then(|mut file| {
327     ///             file.poll_set_len(10)
328     ///         })
329     ///         .map(|res| {
330     ///             // handle returned result ..
331     ///             # println!("{:?}", res);
332     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
333     ///
334     ///     tokio::run(task);
335     /// }
336     /// ```
poll_set_len(&mut self, size: u64) -> Poll<(), io::Error>337     pub fn poll_set_len(&mut self, size: u64) -> Poll<(), io::Error> {
338         ::blocking_io(|| self.std().set_len(size))
339     }
340 
341     /// Queries metadata about the underlying file.
342     ///
343     /// # Examples
344     ///
345     /// ```no_run
346     /// # extern crate tokio;
347     /// use tokio::prelude::Future;
348     ///
349     /// fn main() {
350     ///     let task = tokio::fs::File::create("foo.txt")
351     ///         .and_then(|file| file.metadata())
352     ///         .map(|metadata| {
353     ///             println!("{:?}", metadata);
354     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
355     ///
356     ///     tokio::run(task);
357     /// }
358     /// ```
metadata(self) -> MetadataFuture359     pub fn metadata(self) -> MetadataFuture {
360         MetadataFuture::new(self)
361     }
362 
363     /// Queries metadata about the underlying file.
364     ///
365     /// # Examples
366     ///
367     /// ```no_run
368     /// # extern crate tokio;
369     /// use tokio::prelude::Future;
370     ///
371     /// fn main() {
372     ///     let task = tokio::fs::File::create("foo.txt")
373     ///         .and_then(|mut file| file.poll_metadata())
374     ///         .map(|metadata| {
375     ///             // metadata is of type Async::Ready<Metadata>
376     ///             println!("{:?}", metadata);
377     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
378     ///
379     ///     tokio::run(task);
380     /// }
381     /// ```
poll_metadata(&mut self) -> Poll<Metadata, io::Error>382     pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
383         ::blocking_io(|| self.std().metadata())
384     }
385 
386     /// Create a new `File` instance that shares the same underlying file handle
387     /// as the existing `File` instance. Reads, writes, and seeks will affect both
388     /// File instances simultaneously.
389     ///
390     /// # Examples
391     ///
392     /// ```no_run
393     /// # extern crate tokio;
394     /// use tokio::prelude::Future;
395     ///
396     /// fn main() {
397     ///     let task = tokio::fs::File::create("foo.txt")
398     ///         .and_then(|mut file| file.poll_try_clone())
399     ///         .map(|clone| {
400     ///             // do something with the clone
401     ///             # println!("{:?}", clone);
402     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
403     ///
404     ///     tokio::run(task);
405     /// }
406     /// ```
poll_try_clone(&mut self) -> Poll<File, io::Error>407     pub fn poll_try_clone(&mut self) -> Poll<File, io::Error> {
408         ::blocking_io(|| {
409             let std = self.std().try_clone()?;
410             Ok(File::from_std(std))
411         })
412     }
413 
414     /// Create a new `File` instance that shares the same underlying file handle
415     /// as the existing `File` instance. Reads, writes, and seeks will affect both
416     /// File instances simultaneously.
417     ///
418     /// # Examples
419     /// ```no_run
420     /// # extern crate tokio;
421     /// use tokio::prelude::Future;
422     ///
423     /// fn main() {
424     ///     let task = tokio::fs::File::create("foo.txt")
425     ///         .and_then(|file| {
426     ///             file.try_clone()
427     ///                 .map(|(file, clone)| {
428     ///                     // do something with the file and the clone
429     ///                     # println!("{:?} {:?}", file, clone);
430     ///                 })
431     ///                 .map_err(|(file, err)| {
432     ///                     // you get the original file back if there's an error
433     ///                     # println!("{:?}", file);
434     ///                     err
435     ///                 })
436     ///         })
437     ///         .map_err(|err| eprintln!("IO error: {:?}", err));
438     ///
439     ///     tokio::run(task);
440     /// }
441     /// ```
try_clone(self) -> CloneFuture442     pub fn try_clone(self) -> CloneFuture {
443         CloneFuture::new(self)
444     }
445 
446     /// Changes the permissions on the underlying file.
447     ///
448     /// # Platform-specific behavior
449     ///
450     /// This function currently corresponds to the `fchmod` function on Unix and
451     /// the `SetFileInformationByHandle` function on Windows. Note that, this
452     /// [may change in the future][changes].
453     ///
454     /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
455     ///
456     /// # Errors
457     ///
458     /// This function will return an error if the user lacks permission change
459     /// attributes on the underlying file. It may also return an error in other
460     /// os-specific unspecified cases.
461     ///
462     /// # Examples
463     ///
464     /// ```no_run
465     /// # extern crate tokio;
466     /// use tokio::prelude::Future;
467     ///
468     /// fn main() {
469     ///     let task = tokio::fs::File::create("foo.txt")
470     ///         .and_then(|file| file.metadata())
471     ///         .map(|(mut file, metadata)| {
472     ///             let mut perms = metadata.permissions();
473     ///             perms.set_readonly(true);
474     ///             match file.poll_set_permissions(perms) {
475     ///                 Err(e) => eprintln!("{}", e),
476     ///                 _ => println!("permissions set!"),
477     ///             }
478     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
479     ///
480     ///     tokio::run(task);
481     /// }
482     /// ```
poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error>483     pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error> {
484         ::blocking_io(|| self.std().set_permissions(perm))
485     }
486 
487     /// Destructures the `tokio_fs::File` into a [`std::fs::File`][std].
488     ///
489     /// # Panics
490     ///
491     /// This function will panic if `shutdown` has been called.
492     ///
493     /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
494     ///
495     /// # Examples
496     ///
497     /// ```no_run
498     /// # extern crate tokio;
499     /// use tokio::prelude::Future;
500     ///
501     /// fn main() {
502     ///     let task = tokio::fs::File::create("foo.txt")
503     ///         .map(|file| {
504     ///             let std_file = file.into_std();
505     ///             // do something with the std::fs::File
506     ///             # println!("{:?}", std_file);
507     ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
508     ///
509     ///     tokio::run(task);
510     /// }
511     /// ```
into_std(mut self) -> StdFile512     pub fn into_std(mut self) -> StdFile {
513         self.std.take().expect("`File` instance already shutdown")
514     }
515 
std(&mut self) -> &mut StdFile516     fn std(&mut self) -> &mut StdFile {
517         self.std.as_mut().expect("`File` instance already shutdown")
518     }
519 }
520 
521 impl Read for File {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>522     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
523         ::would_block(|| self.std().read(buf))
524     }
525 }
526 
527 impl AsyncRead for File {
prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool528     unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
529         false
530     }
531 }
532 
533 impl Write for File {
write(&mut self, buf: &[u8]) -> io::Result<usize>534     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
535         ::would_block(|| self.std().write(buf))
536     }
537 
flush(&mut self) -> io::Result<()>538     fn flush(&mut self) -> io::Result<()> {
539         ::would_block(|| self.std().flush())
540     }
541 }
542 
543 impl AsyncWrite for File {
shutdown(&mut self) -> Poll<(), io::Error>544     fn shutdown(&mut self) -> Poll<(), io::Error> {
545         ::blocking_io(|| {
546             self.std = None;
547             Ok(())
548         })
549     }
550 }
551 
552 impl Drop for File {
drop(&mut self)553     fn drop(&mut self) {
554         if let Some(_std) = self.std.take() {
555             // This is probably fine as closing a file *shouldn't* be a blocking
556             // operation. That said, ideally `shutdown` is called first.
557         }
558     }
559 }
560