]> git.lizzy.rs Git - rust.git/blob - src/libstd/fs/mod.rs
Rollup merge of #22744 - alexcrichton:issue-22738, r=aturon
[rust.git] / src / libstd / fs / mod.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Filesystem manipulation operations
12 //!
13 //! This module contains basic methods to manipulate the contents of the local
14 //! filesystem. All methods in this module represent cross-platform filesystem
15 //! operations. Extra platform-specific functionality can be found in the
16 //! extension traits of `std::os::$platform`.
17
18 #![unstable(feature = "fs")]
19
20 use core::prelude::*;
21
22 use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write};
23 use path::{AsPath, Path, PathBuf};
24 use sys::fs2 as fs_imp;
25 use sys_common::{AsInnerMut, FromInner, AsInner};
26 use vec::Vec;
27
28 pub use self::tempdir::TempDir;
29
30 mod tempdir;
31
32 /// A reference to an open file on the filesystem.
33 ///
34 /// An instance of a `File` can be read and/or written depending on what options
35 /// it was opened with. Files also implement `Seek` to alter the logical cursor
36 /// that the file contains internally.
37 ///
38 /// # Example
39 ///
40 /// ```no_run
41 /// use std::io::prelude::*;
42 /// use std::fs::File;
43 ///
44 /// # fn foo() -> std::io::Result<()> {
45 /// let mut f = try!(File::create("foo.txt"));
46 /// try!(f.write_all(b"Hello, world!"));
47 ///
48 /// let mut f = try!(File::open("foo.txt"));
49 /// let mut s = String::new();
50 /// try!(f.read_to_string(&mut s));
51 /// assert_eq!(s, "Hello, world!");
52 /// # Ok(())
53 /// # }
54 /// ```
55 pub struct File {
56     inner: fs_imp::File,
57     path: PathBuf,
58 }
59
60 /// Metadata information about a file.
61 ///
62 /// This structure is returned from the `metadata` function or method and
63 /// represents known metadata about a file such as its permissions, size,
64 /// modification times, etc.
65 pub struct Metadata(fs_imp::FileAttr);
66
67 /// Iterator over the entries in a directory.
68 ///
69 /// This iterator is returned from the `read_dir` function of this module and
70 /// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry`
71 /// information like the entry's path and possibly other metadata can be
72 /// learned.
73 pub struct ReadDir(fs_imp::ReadDir);
74
75 /// Entries returned by the `ReadDir` iterator.
76 ///
77 /// An instance of `DirEntry` represents an entry inside of a directory on the
78 /// filesystem. Each entry can be inspected via methods to learn about the full
79 /// path or possibly other metadata through per-platform extension traits.
80 pub struct DirEntry(fs_imp::DirEntry);
81
82 /// An iterator that recursively walks over the contents of a directory.
83 pub struct WalkDir {
84     cur: Option<ReadDir>,
85     stack: Vec<io::Result<ReadDir>>,
86 }
87
88 /// Options and flags which can be used to configure how a file is opened.
89 ///
90 /// This builder exposes the ability to configure how a `File` is opened and
91 /// what operations are permitted on the open file. The `File::open` and
92 /// `File::create` methods are aliases for commonly used options using this
93 /// builder.
94 #[derive(Clone)]
95 pub struct OpenOptions(fs_imp::OpenOptions);
96
97 /// Representation of the various permissions on a file.
98 ///
99 /// This module only currently provides one bit of information, `readonly`,
100 /// which is exposed on all currently supported platforms. Unix-specific
101 /// functionality, such as mode bits, is available through the
102 /// `os::unix::PermissionsExt` trait.
103 #[derive(Clone, PartialEq, Eq, Debug)]
104 pub struct Permissions(fs_imp::FilePermissions);
105
106 impl File {
107     /// Attempts to open a file in read-only mode.
108     ///
109     /// See the `OpenOptions::open` method for more details.
110     ///
111     /// # Errors
112     ///
113     /// This function will return an error if `path` does not already exist.
114     /// Other errors may also be returned according to `OpenOptions::open`.
115     pub fn open<P: AsPath + ?Sized>(path: &P) -> io::Result<File> {
116         OpenOptions::new().read(true).open(path)
117     }
118
119     /// Open a file in write-only mode.
120     ///
121     /// This function will create a file if it does not exist,
122     /// and will truncate it if it does.
123     ///
124     /// See the `OpenOptions::open` function for more details.
125     pub fn create<P: AsPath + ?Sized>(path: &P) -> io::Result<File> {
126         OpenOptions::new().write(true).create(true).truncate(true).open(path)
127     }
128
129     /// Returns the original path that was used to open this file.
130     pub fn path(&self) -> Option<&Path> {
131         Some(&self.path)
132     }
133
134     /// Attempt to sync all OS-internal metadata to disk.
135     ///
136     /// This function will attempt to ensure that all in-core data reaches the
137     /// filesystem before returning.
138     pub fn sync_all(&self) -> io::Result<()> {
139         self.inner.fsync()
140     }
141
142     /// This function is similar to `sync_all`, except that it may not
143     /// synchronize file metadata to the filesystem.
144     ///
145     /// This is intended for use cases that must synchronize content, but don't
146     /// need the metadata on disk. The goal of this method is to reduce disk
147     /// operations.
148     ///
149     /// Note that some platforms may simply implement this in terms of
150     /// `sync_all`.
151     pub fn sync_data(&self) -> io::Result<()> {
152         self.inner.datasync()
153     }
154
155     /// Truncates or extends the underlying file, updating the size of
156     /// this file to become `size`.
157     ///
158     /// If the `size` is less than the current file's size, then the file will
159     /// be shrunk. If it is greater than the current file's size, then the file
160     /// will be extended to `size` and have all of the intermediate data filled
161     /// in with 0s.
162     pub fn set_len(&self, size: u64) -> io::Result<()> {
163         self.inner.truncate(size)
164     }
165
166     /// Queries information about the underlying file.
167     pub fn metadata(&self) -> io::Result<Metadata> {
168         self.inner.file_attr().map(Metadata)
169     }
170 }
171
172 impl AsInner<fs_imp::File> for File {
173     fn as_inner(&self) -> &fs_imp::File { &self.inner }
174 }
175 impl Read for File {
176     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
177         self.inner.read(buf)
178     }
179 }
180 impl Write for File {
181     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
182         self.inner.write(buf)
183     }
184     fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
185 }
186 impl Seek for File {
187     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
188         self.inner.seek(pos)
189     }
190 }
191 impl<'a> Read for &'a File {
192     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
193         self.inner.read(buf)
194     }
195 }
196 impl<'a> Write for &'a File {
197     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
198         self.inner.write(buf)
199     }
200     fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
201 }
202 impl<'a> Seek for &'a File {
203     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
204         self.inner.seek(pos)
205     }
206 }
207
208 impl OpenOptions {
209     /// Creates a blank net set of options ready for configuration.
210     ///
211     /// All options are initially set to `false`.
212     pub fn new() -> OpenOptions {
213         OpenOptions(fs_imp::OpenOptions::new())
214     }
215
216     /// Set the option for read access.
217     ///
218     /// This option, when true, will indicate that the file should be
219     /// `read`-able if opened.
220     pub fn read(&mut self, read: bool) -> &mut OpenOptions {
221         self.0.read(read); self
222     }
223
224     /// Set the option for write access.
225     ///
226     /// This option, when true, will indicate that the file should be
227     /// `write`-able if opened.
228     pub fn write(&mut self, write: bool) -> &mut OpenOptions {
229         self.0.write(write); self
230     }
231
232     /// Set the option for the append mode.
233     ///
234     /// This option, when true, means that writes will append to a file instead
235     /// of overwriting previous contents.
236     pub fn append(&mut self, append: bool) -> &mut OpenOptions {
237         self.0.append(append); self
238     }
239
240     /// Set the option for truncating a previous file.
241     ///
242     /// If a file is successfully opened with this option set it will truncate
243     /// the file to 0 length if it already exists.
244     pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
245         self.0.truncate(truncate); self
246     }
247
248     /// Set the option for creating a new file.
249     ///
250     /// This option indicates whether a new file will be created if the file
251     /// does not yet already exist.
252     pub fn create(&mut self, create: bool) -> &mut OpenOptions {
253         self.0.create(create); self
254     }
255
256     /// Open a file at `path` with the options specified by `self`.
257     ///
258     /// # Errors
259     ///
260     /// This function will return an error under a number of different
261     /// circumstances, to include but not limited to:
262     ///
263     /// * Opening a file that does not exist with read access.
264     /// * Attempting to open a file with access that the user lacks
265     ///   permissions for
266     /// * Filesystem-level errors (full disk, etc)
267     pub fn open<P: AsPath + ?Sized>(&self, path: &P) -> io::Result<File> {
268         let path = path.as_path();
269         let inner = try!(fs_imp::File::open(path, &self.0));
270
271         // On *BSD systems, we can open a directory as a file and read from
272         // it: fd=open("/tmp", O_RDONLY); read(fd, buf, N); due to an old
273         // tradition before the introduction of opendir(3).  We explicitly
274         // reject it because there are few use cases.
275         if cfg!(not(any(target_os = "linux", target_os = "android"))) &&
276            try!(inner.file_attr()).is_dir() {
277             Err(Error::new(ErrorKind::InvalidInput, "is a directory", None))
278         } else {
279             Ok(File { path: path.to_path_buf(), inner: inner })
280         }
281     }
282 }
283 impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions {
284     fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 }
285 }
286
287 impl Metadata {
288     /// Returns whether this metadata is for a directory.
289     pub fn is_dir(&self) -> bool { self.0.is_dir() }
290
291     /// Returns whether this metadata is for a regular file.
292     pub fn is_file(&self) -> bool { self.0.is_file() }
293
294     /// Returns the size of the file, in bytes, this metadata is for.
295     pub fn len(&self) -> u64 { self.0.size() }
296
297     /// Returns the permissions of the file this metadata is for.
298     pub fn permissions(&self) -> Permissions {
299         Permissions(self.0.perm())
300     }
301
302     /// Returns the most recent access time for a file.
303     ///
304     /// The return value is in milliseconds since the epoch.
305     pub fn accessed(&self) -> u64 { self.0.accessed() }
306
307     /// Returns the most recent modification time for a file.
308     ///
309     /// The return value is in milliseconds since the epoch.
310     pub fn modified(&self) -> u64 { self.0.modified() }
311 }
312
313 impl Permissions {
314     /// Returns whether these permissions describe a readonly file.
315     pub fn readonly(&self) -> bool { self.0.readonly() }
316
317     /// Modify the readonly flag for this set of permissions.
318     ///
319     /// This operation does **not** modify the filesystem. To modify the
320     /// filesystem use the `fs::set_permissions` function.
321     pub fn set_readonly(&mut self, readonly: bool) {
322         self.0.set_readonly(readonly)
323     }
324 }
325
326 impl FromInner<fs_imp::FilePermissions> for Permissions {
327     fn from_inner(f: fs_imp::FilePermissions) -> Permissions {
328         Permissions(f)
329     }
330 }
331
332 impl AsInner<fs_imp::FilePermissions> for Permissions {
333     fn as_inner(&self) -> &fs_imp::FilePermissions { &self.0 }
334 }
335
336 impl Iterator for ReadDir {
337     type Item = io::Result<DirEntry>;
338
339     fn next(&mut self) -> Option<io::Result<DirEntry>> {
340         self.0.next().map(|entry| entry.map(DirEntry))
341     }
342 }
343
344 impl DirEntry {
345     /// Returns the full path to the file that this entry represents.
346     ///
347     /// The full path is created by joining the original path to `read_dir` or
348     /// `walk_dir` with the filename of this entry.
349     pub fn path(&self) -> PathBuf { self.0.path() }
350 }
351
352 /// Remove a file from the underlying filesystem.
353 ///
354 /// # Example
355 ///
356 /// ```rust,no_run
357 /// use std::fs;
358 ///
359 /// fs::remove_file("/some/file/path.txt");
360 /// ```
361 ///
362 /// Note that, just because an unlink call was successful, it is not
363 /// guaranteed that a file is immediately deleted (e.g. depending on
364 /// platform, other open file descriptors may prevent immediate removal).
365 ///
366 /// # Errors
367 ///
368 /// This function will return an error if `path` points to a directory, if the
369 /// user lacks permissions to remove the file, or if some other filesystem-level
370 /// error occurs.
371 pub fn remove_file<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
372     let path = path.as_path();
373     let e = match fs_imp::unlink(path) {
374         Ok(()) => return Ok(()),
375         Err(e) => e,
376     };
377     if !cfg!(windows) { return Err(e) }
378
379     // On unix, a readonly file can be successfully removed. On windows,
380     // however, it cannot. To keep the two platforms in line with
381     // respect to their behavior, catch this case on windows, attempt to
382     // change it to read-write, and then remove the file.
383     if e.kind() != ErrorKind::PermissionDenied { return Err(e) }
384
385     let attr = match metadata(path) { Ok(a) => a, Err(..) => return Err(e) };
386     let mut perms = attr.permissions();
387     if !perms.readonly() { return Err(e) }
388     perms.set_readonly(false);
389
390     if set_permissions(path, perms).is_err() { return Err(e) }
391     if fs_imp::unlink(path).is_ok() { return Ok(()) }
392
393     // Oops, try to put things back the way we found it
394     let _ = set_permissions(path, attr.permissions());
395     Err(e)
396 }
397
398 /// Given a path, query the file system to get information about a file,
399 /// directory, etc.
400 ///
401 /// This function will traverse soft links to query information about the
402 /// destination file.
403 ///
404 /// # Example
405 ///
406 /// ```rust,no_run
407 /// # fn foo() -> std::io::Result<()> {
408 /// use std::fs;
409 ///
410 /// let attr = try!(fs::metadata("/some/file/path.txt"));
411 /// // inspect attr ...
412 /// # Ok(())
413 /// # }
414 /// ```
415 ///
416 /// # Errors
417 ///
418 /// This function will return an error if the user lacks the requisite
419 /// permissions to perform a `metadata` call on the given `path` or if there
420 /// is no entry in the filesystem at the provided path.
421 pub fn metadata<P: AsPath + ?Sized>(path: &P) -> io::Result<Metadata> {
422     fs_imp::stat(path.as_path()).map(Metadata)
423 }
424
425 /// Rename a file or directory to a new name.
426 ///
427 /// # Example
428 ///
429 /// ```rust,no_run
430 /// use std::fs;
431 ///
432 /// fs::rename("foo", "bar");
433 /// ```
434 ///
435 /// # Errors
436 ///
437 /// This function will return an error if the provided `from` doesn't exist, if
438 /// the process lacks permissions to view the contents, if `from` and `to`
439 /// reside on separate filesystems, or if some other intermittent I/O error
440 /// occurs.
441 pub fn rename<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
442                                                       -> io::Result<()> {
443     fs_imp::rename(from.as_path(), to.as_path())
444 }
445
446 /// Copies the contents of one file to another. This function will also
447 /// copy the permission bits of the original file to the destination file.
448 ///
449 /// This function will **overwrite** the contents of `to`.
450 ///
451 /// Note that if `from` and `to` both point to the same file, then the file
452 /// will likely get truncated by this operation.
453 ///
454 /// # Example
455 ///
456 /// ```rust
457 /// use std::fs;
458 ///
459 /// fs::copy("foo.txt", "bar.txt");
460 /// ```
461 ///
462 /// # Errors
463 ///
464 /// This function will return an error in the following situations, but is not
465 /// limited to just these cases:
466 ///
467 /// * The `from` path is not a file
468 /// * The `from` file does not exist
469 /// * The current process does not have the permission rights to access
470 ///   `from` or write `to`
471 pub fn copy<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
472                                                     -> io::Result<u64> {
473     let from = from.as_path();
474     if !from.is_file() {
475         return Err(Error::new(ErrorKind::MismatchedFileTypeForOperation,
476                               "the source path is not an existing file",
477                               None))
478     }
479
480     let mut reader = try!(File::open(from));
481     let mut writer = try!(File::create(to));
482     let perm = try!(reader.metadata()).permissions();
483
484     let ret = try!(io::copy(&mut reader, &mut writer));
485     try!(set_permissions(to, perm));
486     Ok(ret)
487 }
488
489 /// Creates a new hard link on the filesystem.
490 ///
491 /// The `dst` path will be a link pointing to the `src` path. Note that systems
492 /// often require these two paths to both be located on the same filesystem.
493 pub fn hard_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
494                                                          -> io::Result<()> {
495     fs_imp::link(src.as_path(), dst.as_path())
496 }
497
498 /// Creates a new soft link on the filesystem.
499 ///
500 /// The `dst` path will be a soft link pointing to the `src` path.
501 pub fn soft_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
502                                                          -> io::Result<()> {
503     fs_imp::symlink(src.as_path(), dst.as_path())
504 }
505
506 /// Reads a soft link, returning the file that the link points to.
507 ///
508 /// # Errors
509 ///
510 /// This function will return an error on failure. Failure conditions include
511 /// reading a file that does not exist or reading a file that is not a soft
512 /// link.
513 pub fn read_link<P: AsPath + ?Sized>(path: &P) -> io::Result<PathBuf> {
514     fs_imp::readlink(path.as_path())
515 }
516
517 /// Create a new, empty directory at the provided path
518 ///
519 /// # Example
520 ///
521 /// ```rust
522 /// use std::fs;
523 ///
524 /// fs::create_dir("/some/dir");
525 /// ```
526 ///
527 /// # Errors
528 ///
529 /// This function will return an error if the user lacks permissions to make a
530 /// new directory at the provided `path`, or if the directory already exists.
531 pub fn create_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
532     fs_imp::mkdir(path.as_path())
533 }
534
535 /// Recursively create a directory and all of its parent components if they
536 /// are missing.
537 ///
538 /// # Errors
539 ///
540 /// This function will fail if any directory in the path specified by `path`
541 /// does not already exist and it could not be created otherwise. The specific
542 /// error conditions for when a directory is being created (after it is
543 /// determined to not exist) are outlined by `fs::create_dir`.
544 pub fn create_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
545     let path = path.as_path();
546     if path.is_dir() { return Ok(()) }
547     match path.parent() {
548         Some(p) if p != path => try!(create_dir_all(p)),
549         _ => {}
550     }
551     // If the file name of the given `path` is blank then the creation of the
552     // parent directory will have taken care of the whole path for us, so we're
553     // good to go.
554     if path.file_name().is_none() {
555         Ok(())
556     } else {
557         create_dir(path)
558     }
559 }
560
561 /// Remove an existing, empty directory
562 ///
563 /// # Example
564 ///
565 /// ```rust
566 /// use std::fs;
567 ///
568 /// fs::remove_dir("/some/dir");
569 /// ```
570 ///
571 /// # Errors
572 ///
573 /// This function will return an error if the user lacks permissions to remove
574 /// the directory at the provided `path`, or if the directory isn't empty.
575 pub fn remove_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
576     fs_imp::rmdir(path.as_path())
577 }
578
579 /// Removes a directory at this path, after removing all its contents. Use
580 /// carefully!
581 ///
582 /// This function does **not** follow soft links and it will simply remove the
583 /// soft link itself.
584 ///
585 /// # Errors
586 ///
587 /// See `file::remove_file` and `fs::remove_dir`
588 pub fn remove_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
589     let path = path.as_path();
590     for child in try!(read_dir(path)) {
591         let child = try!(child).path();
592         let stat = try!(lstat(&*child));
593         if stat.is_dir() {
594             try!(remove_dir_all(&*child));
595         } else {
596             try!(remove_file(&*child));
597         }
598     }
599     return remove_dir(path);
600
601     #[cfg(unix)]
602     fn lstat(path: &Path) -> io::Result<fs_imp::FileAttr> { fs_imp::lstat(path) }
603     #[cfg(windows)]
604     fn lstat(path: &Path) -> io::Result<fs_imp::FileAttr> { fs_imp::stat(path) }
605 }
606
607 /// Returns an iterator over the entries within a directory.
608 ///
609 /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may
610 /// be encountered after an iterator is initially constructed.
611 ///
612 /// # Example
613 ///
614 /// ```rust
615 /// use std::io;
616 /// use std::fs::{self, PathExt, DirEntry};
617 /// use std::path::Path;
618 ///
619 /// // one possible implementation of fs::walk_dir only visiting files
620 /// fn visit_dirs(dir: &Path, cb: &mut FnMut(DirEntry)) -> io::Result<()> {
621 ///     if dir.is_dir() {
622 ///         for entry in try!(fs::read_dir(dir)) {
623 ///             let entry = try!(entry);
624 ///             if entry.path().is_dir() {
625 ///                 try!(visit_dirs(&entry.path(), cb));
626 ///             } else {
627 ///                 cb(entry);
628 ///             }
629 ///         }
630 ///     }
631 ///     Ok(())
632 /// }
633 /// ```
634 ///
635 /// # Errors
636 ///
637 /// This function will return an error if the provided `path` doesn't exist, if
638 /// the process lacks permissions to view the contents or if the `path` points
639 /// at a non-directory file
640 pub fn read_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<ReadDir> {
641     fs_imp::readdir(path.as_path()).map(ReadDir)
642 }
643
644 /// Returns an iterator that will recursively walk the directory structure
645 /// rooted at `path`.
646 ///
647 /// The path given will not be iterated over, and this will perform iteration in
648 /// some top-down order.  The contents of unreadable subdirectories are ignored.
649 ///
650 /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may
651 /// be encountered after an iterator is initially constructed.
652 pub fn walk_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<WalkDir> {
653     let start = try!(read_dir(path));
654     Ok(WalkDir { cur: Some(start), stack: Vec::new() })
655 }
656
657 impl Iterator for WalkDir {
658     type Item = io::Result<DirEntry>;
659
660     fn next(&mut self) -> Option<io::Result<DirEntry>> {
661         loop {
662             if let Some(ref mut cur) = self.cur {
663                 match cur.next() {
664                     Some(Err(e)) => return Some(Err(e)),
665                     Some(Ok(next)) => {
666                         let path = next.path();
667                         if path.is_dir() {
668                             self.stack.push(read_dir(&*path));
669                         }
670                         return Some(Ok(next))
671                     }
672                     None => {}
673                 }
674             }
675             self.cur = None;
676             match self.stack.pop() {
677                 Some(Err(e)) => return Some(Err(e)),
678                 Some(Ok(next)) => self.cur = Some(next),
679                 None => return None,
680             }
681         }
682     }
683 }
684
685 /// Utility methods for paths.
686 pub trait PathExt {
687     /// Get information on the file, directory, etc at this path.
688     ///
689     /// Consult the `fs::stat` documentation for more info.
690     ///
691     /// This call preserves identical runtime/error semantics with `file::stat`.
692     fn metadata(&self) -> io::Result<Metadata>;
693
694     /// Boolean value indicator whether the underlying file exists on the local
695     /// filesystem. Returns false in exactly the cases where `fs::stat` fails.
696     fn exists(&self) -> bool;
697
698     /// Whether the underlying implementation (be it a file path, or something
699     /// else) points at a "regular file" on the FS. Will return false for paths
700     /// to non-existent locations or directories or other non-regular files
701     /// (named pipes, etc). Follows links when making this determination.
702     fn is_file(&self) -> bool;
703
704     /// Whether the underlying implementation (be it a file path, or something
705     /// else) is pointing at a directory in the underlying FS. Will return
706     /// false for paths to non-existent locations or if the item is not a
707     /// directory (eg files, named pipes, etc). Follows links when making this
708     /// determination.
709     fn is_dir(&self) -> bool;
710 }
711
712 impl PathExt for Path {
713     fn metadata(&self) -> io::Result<Metadata> { metadata(self) }
714
715     fn exists(&self) -> bool { metadata(self).is_ok() }
716
717     fn is_file(&self) -> bool {
718         metadata(self).map(|s| s.is_file()).unwrap_or(false)
719     }
720     fn is_dir(&self) -> bool {
721         metadata(self).map(|s| s.is_dir()).unwrap_or(false)
722     }
723 }
724
725 /// Changes the timestamps for a file's last modification and access time.
726 ///
727 /// The file at the path specified will have its last access time set to
728 /// `atime` and its modification time set to `mtime`. The times specified should
729 /// be in milliseconds.
730 pub fn set_file_times<P: AsPath + ?Sized>(path: &P, accessed: u64,
731                                           modified: u64) -> io::Result<()> {
732     fs_imp::utimes(path.as_path(), accessed, modified)
733 }
734
735 /// Changes the permissions found on a file or a directory.
736 ///
737 /// # Example
738 ///
739 /// ```
740 /// # fn foo() -> std::io::Result<()> {
741 /// use std::fs;
742 ///
743 /// let mut perms = try!(fs::metadata("foo.txt")).permissions();
744 /// perms.set_readonly(true);
745 /// try!(fs::set_permissions("foo.txt", perms));
746 /// # Ok(())
747 /// # }
748 /// ```
749 ///
750 /// # Errors
751 ///
752 /// This function will return an error if the provided `path` doesn't exist, if
753 /// the process lacks permissions to change the attributes of the file, or if
754 /// some other I/O error is encountered.
755 pub fn set_permissions<P: AsPath + ?Sized>(path: &P, perm: Permissions)
756                                            -> io::Result<()> {
757     fs_imp::set_perm(path.as_path(), perm.0)
758 }
759
760 #[cfg(test)]
761 mod tests {
762     #![allow(deprecated)] //rand
763
764     use prelude::v1::*;
765     use io::prelude::*;
766
767     use fs::{self, File, OpenOptions};
768     use io::{ErrorKind, SeekFrom};
769     use path::PathBuf;
770     use path::Path as Path2;
771     use os;
772     use rand::{self, StdRng, Rng};
773     use str;
774
775     macro_rules! check { ($e:expr) => (
776         match $e {
777             Ok(t) => t,
778             Err(e) => panic!("{} failed with: {}", stringify!($e), e),
779         }
780     ) }
781
782     macro_rules! error { ($e:expr, $s:expr) => (
783         match $e {
784             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
785             Err(ref err) => assert!(err.to_string().contains($s.as_slice()),
786                                     format!("`{}` did not contain `{}`", err, $s))
787         }
788     ) }
789
790     pub struct TempDir(PathBuf);
791
792     impl TempDir {
793         fn join(&self, path: &str) -> PathBuf {
794             let TempDir(ref p) = *self;
795             p.join(path)
796         }
797
798         fn path<'a>(&'a self) -> &'a Path2 {
799             let TempDir(ref p) = *self;
800             p
801         }
802     }
803
804     impl Drop for TempDir {
805         fn drop(&mut self) {
806             // Gee, seeing how we're testing the fs module I sure hope that we
807             // at least implement this correctly!
808             let TempDir(ref p) = *self;
809             check!(fs::remove_dir_all(p));
810         }
811     }
812
813     pub fn tmpdir() -> TempDir {
814         let s = os::tmpdir();
815         let p = Path2::new(s.as_str().unwrap());
816         let ret = p.join(&format!("rust-{}", rand::random::<u32>()));
817         check!(fs::create_dir(&ret));
818         TempDir(ret)
819     }
820
821     #[test]
822     fn file_test_io_smoke_test() {
823         let message = "it's alright. have a good time";
824         let tmpdir = tmpdir();
825         let filename = &tmpdir.join("file_rt_io_file_test.txt");
826         {
827             let mut write_stream = check!(File::create(filename));
828             check!(write_stream.write(message.as_bytes()));
829         }
830         {
831             let mut read_stream = check!(File::open(filename));
832             let mut read_buf = [0; 1028];
833             let read_str = match check!(read_stream.read(&mut read_buf)) {
834                 -1|0 => panic!("shouldn't happen"),
835                 n => str::from_utf8(&read_buf[..n]).unwrap().to_string()
836             };
837             assert_eq!(read_str.as_slice(), message);
838         }
839         check!(fs::remove_file(filename));
840     }
841
842     #[test]
843     fn invalid_path_raises() {
844         let tmpdir = tmpdir();
845         let filename = &tmpdir.join("file_that_does_not_exist.txt");
846         let result = File::open(filename);
847
848         if cfg!(unix) {
849             error!(result, "o such file or directory");
850         }
851         // error!(result, "couldn't open path as file");
852         // error!(result, format!("path={}; mode=open; access=read", filename.display()));
853     }
854
855     #[test]
856     fn file_test_iounlinking_invalid_path_should_raise_condition() {
857         let tmpdir = tmpdir();
858         let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
859
860         let result = fs::remove_file(filename);
861
862         if cfg!(unix) {
863             error!(result, "o such file or directory");
864         }
865         // error!(result, "couldn't unlink path");
866         // error!(result, format!("path={}", filename.display()));
867     }
868
869     #[test]
870     fn file_test_io_non_positional_read() {
871         let message: &str = "ten-four";
872         let mut read_mem = [0; 8];
873         let tmpdir = tmpdir();
874         let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
875         {
876             let mut rw_stream = check!(File::create(filename));
877             check!(rw_stream.write(message.as_bytes()));
878         }
879         {
880             let mut read_stream = check!(File::open(filename));
881             {
882                 let read_buf = &mut read_mem[0..4];
883                 check!(read_stream.read(read_buf));
884             }
885             {
886                 let read_buf = &mut read_mem[4..8];
887                 check!(read_stream.read(read_buf));
888             }
889         }
890         check!(fs::remove_file(filename));
891         let read_str = str::from_utf8(&read_mem).unwrap();
892         assert_eq!(read_str, message);
893     }
894
895     #[test]
896     fn file_test_io_seek_and_tell_smoke_test() {
897         let message = "ten-four";
898         let mut read_mem = [0; 4];
899         let set_cursor = 4 as u64;
900         let mut tell_pos_pre_read;
901         let mut tell_pos_post_read;
902         let tmpdir = tmpdir();
903         let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
904         {
905             let mut rw_stream = check!(File::create(filename));
906             check!(rw_stream.write(message.as_bytes()));
907         }
908         {
909             let mut read_stream = check!(File::open(filename));
910             check!(read_stream.seek(SeekFrom::Start(set_cursor)));
911             tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
912             check!(read_stream.read(&mut read_mem));
913             tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
914         }
915         check!(fs::remove_file(filename));
916         let read_str = str::from_utf8(&read_mem).unwrap();
917         assert_eq!(read_str, &message[4..8]);
918         assert_eq!(tell_pos_pre_read, set_cursor);
919         assert_eq!(tell_pos_post_read, message.len() as u64);
920     }
921
922     #[test]
923     fn file_test_io_seek_and_write() {
924         let initial_msg =   "food-is-yummy";
925         let overwrite_msg =    "-the-bar!!";
926         let final_msg =     "foo-the-bar!!";
927         let seek_idx = 3;
928         let mut read_mem = [0; 13];
929         let tmpdir = tmpdir();
930         let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
931         {
932             let mut rw_stream = check!(File::create(filename));
933             check!(rw_stream.write(initial_msg.as_bytes()));
934             check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
935             check!(rw_stream.write(overwrite_msg.as_bytes()));
936         }
937         {
938             let mut read_stream = check!(File::open(filename));
939             check!(read_stream.read(&mut read_mem));
940         }
941         check!(fs::remove_file(filename));
942         let read_str = str::from_utf8(&read_mem).unwrap();
943         assert!(read_str == final_msg);
944     }
945
946     #[test]
947     fn file_test_io_seek_shakedown() {
948         //                   01234567890123
949         let initial_msg =   "qwer-asdf-zxcv";
950         let chunk_one: &str = "qwer";
951         let chunk_two: &str = "asdf";
952         let chunk_three: &str = "zxcv";
953         let mut read_mem = [0; 4];
954         let tmpdir = tmpdir();
955         let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
956         {
957             let mut rw_stream = check!(File::create(filename));
958             check!(rw_stream.write(initial_msg.as_bytes()));
959         }
960         {
961             let mut read_stream = check!(File::open(filename));
962
963             check!(read_stream.seek(SeekFrom::End(-4)));
964             check!(read_stream.read(&mut read_mem));
965             assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
966
967             check!(read_stream.seek(SeekFrom::Current(-9)));
968             check!(read_stream.read(&mut read_mem));
969             assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
970
971             check!(read_stream.seek(SeekFrom::Start(0)));
972             check!(read_stream.read(&mut read_mem));
973             assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
974         }
975         check!(fs::remove_file(filename));
976     }
977
978     #[test]
979     fn file_test_stat_is_correct_on_is_file() {
980         let tmpdir = tmpdir();
981         let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
982         {
983             let mut opts = OpenOptions::new();
984             let mut fs = check!(opts.read(true).write(true)
985                                     .create(true).open(filename));
986             let msg = "hw";
987             fs.write(msg.as_bytes()).unwrap();
988
989             let fstat_res = check!(fs.metadata());
990             assert!(fstat_res.is_file());
991         }
992         let stat_res_fn = check!(fs::metadata(filename));
993         assert!(stat_res_fn.is_file());
994         let stat_res_meth = check!(filename.metadata());
995         assert!(stat_res_meth.is_file());
996         check!(fs::remove_file(filename));
997     }
998
999     #[test]
1000     fn file_test_stat_is_correct_on_is_dir() {
1001         let tmpdir = tmpdir();
1002         let filename = &tmpdir.join("file_stat_correct_on_is_dir");
1003         check!(fs::create_dir(filename));
1004         let stat_res_fn = check!(fs::metadata(filename));
1005         assert!(stat_res_fn.is_dir());
1006         let stat_res_meth = check!(filename.metadata());
1007         assert!(stat_res_meth.is_dir());
1008         check!(fs::remove_dir(filename));
1009     }
1010
1011     #[test]
1012     fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
1013         let tmpdir = tmpdir();
1014         let dir = &tmpdir.join("fileinfo_false_on_dir");
1015         check!(fs::create_dir(dir));
1016         assert!(dir.is_file() == false);
1017         check!(fs::remove_dir(dir));
1018     }
1019
1020     #[test]
1021     fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
1022         let tmpdir = tmpdir();
1023         let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
1024         check!(check!(File::create(file)).write(b"foo"));
1025         assert!(file.exists());
1026         check!(fs::remove_file(file));
1027         assert!(!file.exists());
1028     }
1029
1030     #[test]
1031     fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
1032         let tmpdir = tmpdir();
1033         let dir = &tmpdir.join("before_and_after_dir");
1034         assert!(!dir.exists());
1035         check!(fs::create_dir(dir));
1036         assert!(dir.exists());
1037         assert!(dir.is_dir());
1038         check!(fs::remove_dir(dir));
1039         assert!(!dir.exists());
1040     }
1041
1042     #[test]
1043     fn file_test_directoryinfo_readdir() {
1044         let tmpdir = tmpdir();
1045         let dir = &tmpdir.join("di_readdir");
1046         check!(fs::create_dir(dir));
1047         let prefix = "foo";
1048         for n in range(0, 3) {
1049             let f = dir.join(&format!("{}.txt", n));
1050             let mut w = check!(File::create(&f));
1051             let msg_str = format!("{}{}", prefix, n.to_string());
1052             let msg = msg_str.as_bytes();
1053             check!(w.write(msg));
1054         }
1055         let files = check!(fs::read_dir(dir));
1056         let mut mem = [0u8; 4];
1057         for f in files {
1058             let f = f.unwrap().path();
1059             {
1060                 let n = f.file_stem().unwrap();
1061                 check!(check!(File::open(&f)).read(&mut mem));
1062                 let read_str = str::from_utf8(&mem).unwrap();
1063                 let expected = format!("{}{}", prefix, n.to_str().unwrap());
1064                 assert_eq!(expected.as_slice(), read_str);
1065             }
1066             check!(fs::remove_file(&f));
1067         }
1068         check!(fs::remove_dir(dir));
1069     }
1070
1071     #[test]
1072     fn file_test_walk_dir() {
1073         let tmpdir = tmpdir();
1074         let dir = &tmpdir.join("walk_dir");
1075         check!(fs::create_dir(dir));
1076
1077         let dir1 = &dir.join("01/02/03");
1078         check!(fs::create_dir_all(dir1));
1079         check!(File::create(&dir1.join("04")));
1080
1081         let dir2 = &dir.join("11/12/13");
1082         check!(fs::create_dir_all(dir2));
1083         check!(File::create(&dir2.join("14")));
1084
1085         let files = check!(fs::walk_dir(dir));
1086         let mut cur = [0u8; 2];
1087         for f in files {
1088             let f = f.unwrap().path();
1089             let stem = f.file_stem().unwrap().to_str().unwrap();
1090             let root = stem.as_bytes()[0] - b'0';
1091             let name = stem.as_bytes()[1] - b'0';
1092             assert!(cur[root as usize] < name);
1093             cur[root as usize] = name;
1094         }
1095
1096         check!(fs::remove_dir_all(dir));
1097     }
1098
1099     #[test]
1100     fn mkdir_path_already_exists_error() {
1101         let tmpdir = tmpdir();
1102         let dir = &tmpdir.join("mkdir_error_twice");
1103         check!(fs::create_dir(dir));
1104         let e = fs::create_dir(dir).err().unwrap();
1105         assert_eq!(e.kind(), ErrorKind::PathAlreadyExists);
1106     }
1107
1108     #[test]
1109     fn recursive_mkdir() {
1110         let tmpdir = tmpdir();
1111         let dir = tmpdir.join("d1/d2");
1112         check!(fs::create_dir_all(&dir));
1113         assert!(dir.is_dir())
1114     }
1115
1116     #[test]
1117     fn recursive_mkdir_failure() {
1118         let tmpdir = tmpdir();
1119         let dir = tmpdir.join("d1");
1120         let file = dir.join("f1");
1121
1122         check!(fs::create_dir_all(&dir));
1123         check!(File::create(&file));
1124
1125         let result = fs::create_dir_all(&file);
1126
1127         assert!(result.is_err());
1128         // error!(result, "couldn't recursively mkdir");
1129         // error!(result, "couldn't create directory");
1130         // error!(result, "mode=0700");
1131         // error!(result, format!("path={}", file.display()));
1132     }
1133
1134     #[test]
1135     fn recursive_mkdir_slash() {
1136         check!(fs::create_dir_all(&Path2::new("/")));
1137     }
1138
1139     // FIXME(#12795) depends on lstat to work on windows
1140     #[cfg(not(windows))]
1141     #[test]
1142     fn recursive_rmdir() {
1143         let tmpdir = tmpdir();
1144         let d1 = tmpdir.join("d1");
1145         let dt = d1.join("t");
1146         let dtt = dt.join("t");
1147         let d2 = tmpdir.join("d2");
1148         let canary = d2.join("do_not_delete");
1149         check!(fs::create_dir_all(&dtt));
1150         check!(fs::create_dir_all(&d2));
1151         check!(check!(File::create(&canary)).write(b"foo"));
1152         check!(fs::soft_link(&d2, &dt.join("d2")));
1153         check!(fs::remove_dir_all(&d1));
1154
1155         assert!(!d1.is_dir());
1156         assert!(canary.exists());
1157     }
1158
1159     #[test]
1160     fn unicode_path_is_dir() {
1161         assert!(Path2::new(".").is_dir());
1162         assert!(!Path2::new("test/stdtest/fs.rs").is_dir());
1163
1164         let tmpdir = tmpdir();
1165
1166         let mut dirpath = tmpdir.path().to_path_buf();
1167         dirpath.push(&format!("test-가一ー你好"));
1168         check!(fs::create_dir(&dirpath));
1169         assert!(dirpath.is_dir());
1170
1171         let mut filepath = dirpath;
1172         filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
1173         check!(File::create(&filepath)); // ignore return; touch only
1174         assert!(!filepath.is_dir());
1175         assert!(filepath.exists());
1176     }
1177
1178     #[test]
1179     fn unicode_path_exists() {
1180         assert!(Path2::new(".").exists());
1181         assert!(!Path2::new("test/nonexistent-bogus-path").exists());
1182
1183         let tmpdir = tmpdir();
1184         let unicode = tmpdir.path();
1185         let unicode = unicode.join(&format!("test-각丁ー再见"));
1186         check!(fs::create_dir(&unicode));
1187         assert!(unicode.exists());
1188         assert!(!Path2::new("test/unicode-bogus-path-각丁ー再见").exists());
1189     }
1190
1191     #[test]
1192     fn copy_file_does_not_exist() {
1193         let from = Path2::new("test/nonexistent-bogus-path");
1194         let to = Path2::new("test/other-bogus-path");
1195
1196         match fs::copy(&from, &to) {
1197             Ok(..) => panic!(),
1198             Err(..) => {
1199                 assert!(!from.exists());
1200                 assert!(!to.exists());
1201             }
1202         }
1203     }
1204
1205     #[test]
1206     fn copy_file_ok() {
1207         let tmpdir = tmpdir();
1208         let input = tmpdir.join("in.txt");
1209         let out = tmpdir.join("out.txt");
1210
1211         check!(check!(File::create(&input)).write(b"hello"));
1212         check!(fs::copy(&input, &out));
1213         let mut v = Vec::new();
1214         check!(check!(File::open(&out)).read_to_end(&mut v));
1215         assert_eq!(v.as_slice(), b"hello");
1216
1217         assert_eq!(check!(input.metadata()).permissions(),
1218                    check!(out.metadata()).permissions());
1219     }
1220
1221     #[test]
1222     fn copy_file_dst_dir() {
1223         let tmpdir = tmpdir();
1224         let out = tmpdir.join("out");
1225
1226         check!(File::create(&out));
1227         match fs::copy(&*out, tmpdir.path()) {
1228             Ok(..) => panic!(), Err(..) => {}
1229         }
1230     }
1231
1232     #[test]
1233     fn copy_file_dst_exists() {
1234         let tmpdir = tmpdir();
1235         let input = tmpdir.join("in");
1236         let output = tmpdir.join("out");
1237
1238         check!(check!(File::create(&input)).write("foo".as_bytes()));
1239         check!(check!(File::create(&output)).write("bar".as_bytes()));
1240         check!(fs::copy(&input, &output));
1241
1242         let mut v = Vec::new();
1243         check!(check!(File::open(&output)).read_to_end(&mut v));
1244         assert_eq!(v, b"foo".to_vec());
1245     }
1246
1247     #[test]
1248     fn copy_file_src_dir() {
1249         let tmpdir = tmpdir();
1250         let out = tmpdir.join("out");
1251
1252         match fs::copy(tmpdir.path(), &out) {
1253             Ok(..) => panic!(), Err(..) => {}
1254         }
1255         assert!(!out.exists());
1256     }
1257
1258     #[test]
1259     fn copy_file_preserves_perm_bits() {
1260         let tmpdir = tmpdir();
1261         let input = tmpdir.join("in.txt");
1262         let out = tmpdir.join("out.txt");
1263
1264         let attr = check!(check!(File::create(&input)).metadata());
1265         let mut p = attr.permissions();
1266         p.set_readonly(true);
1267         check!(fs::set_permissions(&input, p));
1268         check!(fs::copy(&input, &out));
1269         assert!(check!(out.metadata()).permissions().readonly());
1270     }
1271
1272     #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
1273     #[test]
1274     fn symlinks_work() {
1275         let tmpdir = tmpdir();
1276         let input = tmpdir.join("in.txt");
1277         let out = tmpdir.join("out.txt");
1278
1279         check!(check!(File::create(&input)).write("foobar".as_bytes()));
1280         check!(fs::soft_link(&input, &out));
1281         // if cfg!(not(windows)) {
1282         //     assert_eq!(check!(lstat(&out)).kind, FileType::Symlink);
1283         //     assert_eq!(check!(out.lstat()).kind, FileType::Symlink);
1284         // }
1285         assert_eq!(check!(fs::metadata(&out)).len(),
1286                    check!(fs::metadata(&input)).len());
1287         let mut v = Vec::new();
1288         check!(check!(File::open(&out)).read_to_end(&mut v));
1289         assert_eq!(v, b"foobar".to_vec());
1290     }
1291
1292     #[cfg(not(windows))] // apparently windows doesn't like symlinks
1293     #[test]
1294     fn symlink_noexist() {
1295         let tmpdir = tmpdir();
1296         // symlinks can point to things that don't exist
1297         check!(fs::soft_link(&tmpdir.join("foo"), &tmpdir.join("bar")));
1298         assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))),
1299                    tmpdir.join("foo"));
1300     }
1301
1302     #[test]
1303     fn readlink_not_symlink() {
1304         let tmpdir = tmpdir();
1305         match fs::read_link(tmpdir.path()) {
1306             Ok(..) => panic!("wanted a failure"),
1307             Err(..) => {}
1308         }
1309     }
1310
1311     #[test]
1312     fn links_work() {
1313         let tmpdir = tmpdir();
1314         let input = tmpdir.join("in.txt");
1315         let out = tmpdir.join("out.txt");
1316
1317         check!(check!(File::create(&input)).write("foobar".as_bytes()));
1318         check!(fs::hard_link(&input, &out));
1319         assert_eq!(check!(fs::metadata(&out)).len(),
1320                    check!(fs::metadata(&input)).len());
1321         assert_eq!(check!(fs::metadata(&out)).len(),
1322                    check!(input.metadata()).len());
1323         let mut v = Vec::new();
1324         check!(check!(File::open(&out)).read_to_end(&mut v));
1325         assert_eq!(v, b"foobar".to_vec());
1326
1327         // can't link to yourself
1328         match fs::hard_link(&input, &input) {
1329             Ok(..) => panic!("wanted a failure"),
1330             Err(..) => {}
1331         }
1332         // can't link to something that doesn't exist
1333         match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
1334             Ok(..) => panic!("wanted a failure"),
1335             Err(..) => {}
1336         }
1337     }
1338
1339     #[test]
1340     fn chmod_works() {
1341         let tmpdir = tmpdir();
1342         let file = tmpdir.join("in.txt");
1343
1344         check!(File::create(&file));
1345         let attr = check!(fs::metadata(&file));
1346         assert!(!attr.permissions().readonly());
1347         let mut p = attr.permissions();
1348         p.set_readonly(true);
1349         check!(fs::set_permissions(&file, p.clone()));
1350         let attr = check!(fs::metadata(&file));
1351         assert!(attr.permissions().readonly());
1352
1353         match fs::set_permissions(&tmpdir.join("foo"), p) {
1354             Ok(..) => panic!("wanted a panic"),
1355             Err(..) => {}
1356         }
1357     }
1358
1359     #[test]
1360     fn sync_doesnt_kill_anything() {
1361         let tmpdir = tmpdir();
1362         let path = tmpdir.join("in.txt");
1363
1364         let mut file = check!(File::create(&path));
1365         check!(file.sync_all());
1366         check!(file.sync_data());
1367         check!(file.write(b"foo"));
1368         check!(file.sync_all());
1369         check!(file.sync_data());
1370     }
1371
1372     #[test]
1373     fn truncate_works() {
1374         let tmpdir = tmpdir();
1375         let path = tmpdir.join("in.txt");
1376
1377         let mut file = check!(File::create(&path));
1378         check!(file.write(b"foo"));
1379         check!(file.sync_all());
1380
1381         // Do some simple things with truncation
1382         assert_eq!(check!(file.metadata()).len(), 3);
1383         check!(file.set_len(10));
1384         assert_eq!(check!(file.metadata()).len(), 10);
1385         check!(file.write(b"bar"));
1386         check!(file.sync_all());
1387         assert_eq!(check!(file.metadata()).len(), 10);
1388
1389         let mut v = Vec::new();
1390         check!(check!(File::open(&path)).read_to_end(&mut v));
1391         assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1392
1393         // Truncate to a smaller length, don't seek, and then write something.
1394         // Ensure that the intermediate zeroes are all filled in (we're seeked
1395         // past the end of the file).
1396         check!(file.set_len(2));
1397         assert_eq!(check!(file.metadata()).len(), 2);
1398         check!(file.write(b"wut"));
1399         check!(file.sync_all());
1400         assert_eq!(check!(file.metadata()).len(), 9);
1401         let mut v = Vec::new();
1402         check!(check!(File::open(&path)).read_to_end(&mut v));
1403         assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1404     }
1405
1406     #[test]
1407     fn open_flavors() {
1408         use fs::OpenOptions as OO;
1409         fn c<T: Clone>(t: &T) -> T { t.clone() }
1410
1411         let tmpdir = tmpdir();
1412
1413         let mut r = OO::new(); r.read(true);
1414         let mut w = OO::new(); w.write(true);
1415         let mut rw = OO::new(); rw.write(true).read(true);
1416
1417         match r.open(&tmpdir.join("a")) {
1418             Ok(..) => panic!(), Err(..) => {}
1419         }
1420
1421         // Perform each one twice to make sure that it succeeds the second time
1422         // (where the file exists)
1423         check!(c(&w).create(true).open(&tmpdir.join("b")));
1424         assert!(tmpdir.join("b").exists());
1425         check!(c(&w).create(true).open(&tmpdir.join("b")));
1426         check!(w.open(&tmpdir.join("b")));
1427
1428         check!(c(&rw).create(true).open(&tmpdir.join("c")));
1429         assert!(tmpdir.join("c").exists());
1430         check!(c(&rw).create(true).open(&tmpdir.join("c")));
1431         check!(rw.open(&tmpdir.join("c")));
1432
1433         check!(c(&w).append(true).create(true).open(&tmpdir.join("d")));
1434         assert!(tmpdir.join("d").exists());
1435         check!(c(&w).append(true).create(true).open(&tmpdir.join("d")));
1436         check!(c(&w).append(true).open(&tmpdir.join("d")));
1437
1438         check!(c(&rw).append(true).create(true).open(&tmpdir.join("e")));
1439         assert!(tmpdir.join("e").exists());
1440         check!(c(&rw).append(true).create(true).open(&tmpdir.join("e")));
1441         check!(c(&rw).append(true).open(&tmpdir.join("e")));
1442
1443         check!(c(&w).truncate(true).create(true).open(&tmpdir.join("f")));
1444         assert!(tmpdir.join("f").exists());
1445         check!(c(&w).truncate(true).create(true).open(&tmpdir.join("f")));
1446         check!(c(&w).truncate(true).open(&tmpdir.join("f")));
1447
1448         check!(c(&rw).truncate(true).create(true).open(&tmpdir.join("g")));
1449         assert!(tmpdir.join("g").exists());
1450         check!(c(&rw).truncate(true).create(true).open(&tmpdir.join("g")));
1451         check!(c(&rw).truncate(true).open(&tmpdir.join("g")));
1452
1453         check!(check!(File::create(&tmpdir.join("h"))).write("foo".as_bytes()));
1454         check!(r.open(&tmpdir.join("h")));
1455         {
1456             let mut f = check!(r.open(&tmpdir.join("h")));
1457             assert!(f.write("wut".as_bytes()).is_err());
1458         }
1459         assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1460         {
1461             let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1462             check!(f.write("bar".as_bytes()));
1463         }
1464         assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1465         {
1466             let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1467             check!(f.write("bar".as_bytes()));
1468         }
1469         assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1470     }
1471
1472     #[test]
1473     fn utime() {
1474         let tmpdir = tmpdir();
1475         let path = tmpdir.join("a");
1476         check!(File::create(&path));
1477         // These numbers have to be bigger than the time in the day to account
1478         // for timezones Windows in particular will fail in certain timezones
1479         // with small enough values
1480         check!(fs::set_file_times(&path, 100000, 200000));
1481         assert_eq!(check!(path.metadata()).accessed(), 100000);
1482         assert_eq!(check!(path.metadata()).modified(), 200000);
1483     }
1484
1485     #[test]
1486     fn utime_noexist() {
1487         let tmpdir = tmpdir();
1488
1489         match fs::set_file_times(&tmpdir.join("a"), 100, 200) {
1490             Ok(..) => panic!(),
1491             Err(..) => {}
1492         }
1493     }
1494
1495     #[test]
1496     fn binary_file() {
1497         let mut bytes = [0; 1024];
1498         StdRng::new().ok().unwrap().fill_bytes(&mut bytes);
1499
1500         let tmpdir = tmpdir();
1501
1502         check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1503         let mut v = Vec::new();
1504         check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1505         assert!(v == bytes.as_slice());
1506     }
1507
1508     #[test]
1509     fn unlink_readonly() {
1510         let tmpdir = tmpdir();
1511         let path = tmpdir.join("file");
1512         check!(File::create(&path));
1513         let mut perm = check!(fs::metadata(&path)).permissions();
1514         perm.set_readonly(true);
1515         check!(fs::set_permissions(&path, perm));
1516         check!(fs::remove_file(&path));
1517     }
1518
1519     #[test]
1520     fn mkdir_trailing_slash() {
1521         let tmpdir = tmpdir();
1522         let path = tmpdir.join("file");
1523         check!(fs::create_dir_all(&path.join("a/")));
1524     }
1525 }