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