]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/fs.rs
auto merge of #13049 : alexcrichton/rust/io-fill, r=huonw
[rust.git] / src / libstd / io / fs.rs
1 // Copyright 2013 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 /*! Synchronous File I/O
12
13 This module provides a set of functions and traits for working
14 with regular files & directories on a filesystem.
15
16 At the top-level of the module are a set of freestanding functions, associated
17 with various filesystem operations. They all operate on `Path` objects.
18
19 All operations in this module, including those as part of `File` et al
20 block the task during execution. In the event of failure, all functions/methods
21 will return an `IoResult` type with an `Err` value.
22
23 Also included in this module is an implementation block on the `Path` object
24 defined in `std::path::Path`. The impl adds useful methods about inspecting the
25 metadata of a file. This includes getting the `stat` information, reading off
26 particular bits of it, etc.
27
28 # Example
29
30 ```rust
31 # #[allow(unused_must_use)];
32 use std::io::{File, fs};
33
34 let path = Path::new("foo.txt");
35
36 // create the file, whether it exists or not
37 let mut file = File::create(&path);
38 file.write(bytes!("foobar"));
39 # drop(file);
40
41 // open the file in read-only mode
42 let mut file = File::open(&path);
43 file.read_to_end();
44
45 println!("{}", path.stat().unwrap().size);
46 # drop(file);
47 fs::unlink(&path);
48 ```
49
50 */
51
52 use c_str::ToCStr;
53 use clone::Clone;
54 use container::Container;
55 use iter::Iterator;
56 use super::{Reader, Writer, Seek};
57 use super::{SeekStyle, Read, Write, Open, IoError, Truncate,
58             FileMode, FileAccess, FileStat, IoResult, FilePermission};
59 use rt::rtio::{RtioFileStream, IoFactory, LocalIo};
60 use io;
61 use option::{Some, None, Option};
62 use result::{Ok, Err};
63 use path;
64 use path::{Path, GenericPath};
65 use slice::{OwnedVector, ImmutableVector};
66 use vec::Vec;
67
68 /// Unconstrained file access type that exposes read and write operations
69 ///
70 /// Can be constructed via `File::open()`, `File::create()`, and
71 /// `File::open_mode()`.
72 ///
73 /// # Error
74 ///
75 /// This type will return errors as an `IoResult<T>` if operations are
76 /// attempted against it for which its underlying file descriptor was not
77 /// configured at creation time, via the `FileAccess` parameter to
78 /// `File::open_mode()`.
79 pub struct File {
80     priv fd: ~RtioFileStream,
81     priv path: Path,
82     priv last_nread: int,
83 }
84
85 impl File {
86     /// Open a file at `path` in the mode specified by the `mode` and `access`
87     /// arguments
88     ///
89     /// # Example
90     ///
91     /// ```rust,should_fail
92     /// use std::io::{File, Open, ReadWrite};
93     ///
94     /// let p = Path::new("/some/file/path.txt");
95     ///
96     /// let file = match File::open_mode(&p, Open, ReadWrite) {
97     ///     Ok(f) => f,
98     ///     Err(e) => fail!("file error: {}", e),
99     /// };
100     /// // do some stuff with that file
101     ///
102     /// // the file will be closed at the end of this block
103     /// ```
104     ///
105     /// `FileMode` and `FileAccess` provide information about the permissions
106     /// context in which a given stream is created. More information about them
107     /// can be found in `std::io`'s docs. If a file is opened with `Write`
108     /// or `ReadWrite` access, then it will be created it does not already
109     /// exist.
110     ///
111     /// Note that, with this function, a `File` is returned regardless of the
112     /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a
113     /// `File` opened as `Read` will return an error at runtime).
114     ///
115     /// # Error
116     ///
117     /// This function will return an error under a number of different
118     /// circumstances, to include but not limited to:
119     ///
120     /// * Opening a file that does not exist with `Read` access.
121     /// * Attempting to open a file with a `FileAccess` that the user lacks
122     ///   permissions for
123     /// * Filesystem-level errors (full disk, etc)
124     pub fn open_mode(path: &Path,
125                      mode: FileMode,
126                      access: FileAccess) -> IoResult<File> {
127         LocalIo::maybe_raise(|io| {
128             io.fs_open(&path.to_c_str(), mode, access).map(|fd| {
129                 File {
130                     path: path.clone(),
131                     fd: fd,
132                     last_nread: -1
133                 }
134             })
135         })
136     }
137
138     /// Attempts to open a file in read-only mode. This function is equivalent to
139     /// `File::open_mode(path, Open, Read)`, and will raise all of the same
140     /// errors that `File::open_mode` does.
141     ///
142     /// For more information, see the `File::open_mode` function.
143     ///
144     /// # Example
145     ///
146     /// ```rust
147     /// use std::io::File;
148     ///
149     /// let contents = File::open(&Path::new("foo.txt")).read_to_end();
150     /// ```
151     pub fn open(path: &Path) -> IoResult<File> {
152         File::open_mode(path, Open, Read)
153     }
154
155     /// Attempts to create a file in write-only mode. This function is
156     /// equivalent to `File::open_mode(path, Truncate, Write)`, and will
157     /// raise all of the same errors that `File::open_mode` does.
158     ///
159     /// For more information, see the `File::open_mode` function.
160     ///
161     /// # Example
162     ///
163     /// ```rust
164     /// # #[allow(unused_must_use)];
165     /// use std::io::File;
166     ///
167     /// let mut f = File::create(&Path::new("foo.txt"));
168     /// f.write(bytes!("This is a sample file"));
169     /// # drop(f);
170     /// # ::std::io::fs::unlink(&Path::new("foo.txt"));
171     /// ```
172     pub fn create(path: &Path) -> IoResult<File> {
173         File::open_mode(path, Truncate, Write)
174     }
175
176     /// Returns the original path which was used to open this file.
177     pub fn path<'a>(&'a self) -> &'a Path {
178         &self.path
179     }
180
181     /// Synchronizes all modifications to this file to its permanent storage
182     /// device. This will flush any internal buffers necessary to perform this
183     /// operation.
184     pub fn fsync(&mut self) -> IoResult<()> {
185         self.fd.fsync()
186     }
187
188     /// This function is similar to `fsync`, except that it may not synchronize
189     /// file metadata to the filesystem. This is intended for use case which
190     /// must synchronize content, but don't need the metadata on disk. The goal
191     /// of this method is to reduce disk operations.
192     pub fn datasync(&mut self) -> IoResult<()> {
193         self.fd.datasync()
194     }
195
196     /// Either truncates or extends the underlying file, updating the size of
197     /// this file to become `size`. This is equivalent to unix's `truncate`
198     /// function.
199     ///
200     /// If the `size` is less than the current file's size, then the file will
201     /// be shrunk. If it is greater than the current file's size, then the file
202     /// will be extended to `size` and have all of the intermediate data filled
203     /// in with 0s.
204     pub fn truncate(&mut self, size: i64) -> IoResult<()> {
205         self.fd.truncate(size)
206     }
207
208     /// Tests whether this stream has reached EOF.
209     ///
210     /// If true, then this file will no longer continue to return data via
211     /// `read`.
212     pub fn eof(&self) -> bool {
213         self.last_nread == 0
214     }
215 }
216
217 /// Unlink a file from the underlying filesystem.
218 ///
219 /// # Example
220 ///
221 /// ```rust
222 /// # #[allow(unused_must_use)];
223 /// use std::io::fs;
224 ///
225 /// let p = Path::new("/some/file/path.txt");
226 /// fs::unlink(&p);
227 /// ```
228 ///
229 /// Note that, just because an unlink call was successful, it is not
230 /// guaranteed that a file is immediately deleted (e.g. depending on
231 /// platform, other open file descriptors may prevent immediate removal)
232 ///
233 /// # Error
234 ///
235 /// This function will return an error if the path points to a directory, the
236 /// user lacks permissions to remove the file, or if some other filesystem-level
237 /// error occurs.
238 pub fn unlink(path: &Path) -> IoResult<()> {
239     LocalIo::maybe_raise(|io| io.fs_unlink(&path.to_c_str()))
240 }
241
242 /// Given a path, query the file system to get information about a file,
243 /// directory, etc. This function will traverse symlinks to query
244 /// information about the destination file.
245 ///
246 /// Returns a fully-filled out stat structure on success, and on failure it
247 /// will return a dummy stat structure (it is expected that the condition
248 /// raised is handled as well).
249 ///
250 /// # Example
251 ///
252 /// ```rust
253 /// use std::io::fs;
254 ///
255 /// let p = Path::new("/some/file/path.txt");
256 /// match fs::stat(&p) {
257 ///     Ok(stat) => { /* ... */ }
258 ///     Err(e) => { /* handle error */ }
259 /// }
260 /// ```
261 ///
262 /// # Error
263 ///
264 /// This call will return an error if the user lacks the requisite permissions
265 /// to perform a `stat` call on the given path or if there is no entry in the
266 /// filesystem at the provided path.
267 pub fn stat(path: &Path) -> IoResult<FileStat> {
268     LocalIo::maybe_raise(|io| {
269         io.fs_stat(&path.to_c_str())
270     })
271 }
272
273 /// Perform the same operation as the `stat` function, except that this
274 /// function does not traverse through symlinks. This will return
275 /// information about the symlink file instead of the file that it points
276 /// to.
277 ///
278 /// # Error
279 ///
280 /// See `stat`
281 pub fn lstat(path: &Path) -> IoResult<FileStat> {
282     LocalIo::maybe_raise(|io| {
283         io.fs_lstat(&path.to_c_str())
284     })
285 }
286
287 /// Rename a file or directory to a new name.
288 ///
289 /// # Example
290 ///
291 /// ```rust
292 /// # #[allow(unused_must_use)];
293 /// use std::io::fs;
294 ///
295 /// fs::rename(&Path::new("foo"), &Path::new("bar"));
296 /// ```
297 ///
298 /// # Error
299 ///
300 /// Will return an error if the provided `path` doesn't exist, the process lacks
301 /// permissions to view the contents, or if some other intermittent I/O error
302 /// occurs.
303 pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
304     LocalIo::maybe_raise(|io| io.fs_rename(&from.to_c_str(), &to.to_c_str()))
305 }
306
307 /// Copies the contents of one file to another. This function will also
308 /// copy the permission bits of the original file to the destination file.
309 ///
310 /// Note that if `from` and `to` both point to the same file, then the file
311 /// will likely get truncated by this operation.
312 ///
313 /// # Example
314 ///
315 /// ```rust
316 /// # #[allow(unused_must_use)];
317 /// use std::io::fs;
318 ///
319 /// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt"));
320 /// ```
321 ///
322 /// # Error
323 ///
324 /// Will return an error in the following situations, but is not limited to
325 /// just these cases:
326 ///
327 /// * The `from` path is not a file
328 /// * The `from` file does not exist
329 /// * The current process does not have the permission rights to access
330 ///   `from` or write `to`
331 ///
332 /// Note that this copy is not atomic in that once the destination is
333 /// ensured to not exist, there is nothing preventing the destination from
334 /// being created and then destroyed by this operation.
335 pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
336     if !from.is_file() {
337         return Err(IoError {
338             kind: io::MismatchedFileTypeForOperation,
339             desc: "the source path is not an existing file",
340             detail: None,
341         })
342     }
343
344     let mut reader = try!(File::open(from));
345     let mut writer = try!(File::create(to));
346     let mut buf = [0, ..io::DEFAULT_BUF_SIZE];
347
348     loop {
349         let amt = match reader.read(buf) {
350             Ok(n) => n,
351             Err(ref e) if e.kind == io::EndOfFile => { break }
352             Err(e) => return Err(e)
353         };
354         try!(writer.write(buf.slice_to(amt)));
355     }
356
357     chmod(to, try!(from.stat()).perm)
358 }
359
360 /// Changes the permission mode bits found on a file or a directory. This
361 /// function takes a mask from the `io` module
362 ///
363 /// # Example
364 ///
365 /// ```rust
366 /// # #[allow(unused_must_use)];
367 /// use std::io;
368 /// use std::io::fs;
369 ///
370 /// fs::chmod(&Path::new("file.txt"), io::UserFile);
371 /// fs::chmod(&Path::new("file.txt"), io::UserRead | io::UserWrite);
372 /// fs::chmod(&Path::new("dir"),      io::UserDir);
373 /// fs::chmod(&Path::new("file.exe"), io::UserExec);
374 /// ```
375 ///
376 /// # Error
377 ///
378 /// If this function encounters an I/O error, it will return an `Err` value.
379 /// Some possible error situations are not having the permission to
380 /// change the attributes of a file or the file not existing.
381 pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
382     LocalIo::maybe_raise(|io| io.fs_chmod(&path.to_c_str(), mode))
383 }
384
385 /// Change the user and group owners of a file at the specified path.
386 pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
387     LocalIo::maybe_raise(|io| io.fs_chown(&path.to_c_str(), uid, gid))
388 }
389
390 /// Creates a new hard link on the filesystem. The `dst` path will be a
391 /// link pointing to the `src` path. Note that systems often require these
392 /// two paths to both be located on the same filesystem.
393 pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
394     LocalIo::maybe_raise(|io| io.fs_link(&src.to_c_str(), &dst.to_c_str()))
395 }
396
397 /// Creates a new symbolic link on the filesystem. The `dst` path will be a
398 /// symlink pointing to the `src` path.
399 pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
400     LocalIo::maybe_raise(|io| io.fs_symlink(&src.to_c_str(), &dst.to_c_str()))
401 }
402
403 /// Reads a symlink, returning the file that the symlink points to.
404 ///
405 /// # Error
406 ///
407 /// This function will return an error on failure. Failure conditions include
408 /// reading a file that does not exist or reading a file which is not a symlink.
409 pub fn readlink(path: &Path) -> IoResult<Path> {
410     LocalIo::maybe_raise(|io| io.fs_readlink(&path.to_c_str()))
411 }
412
413 /// Create a new, empty directory at the provided path
414 ///
415 /// # Example
416 ///
417 /// ```rust
418 /// # #[allow(unused_must_use)];
419 /// use std::io;
420 /// use std::io::fs;
421 ///
422 /// let p = Path::new("/some/dir");
423 /// fs::mkdir(&p, io::UserRWX);
424 /// ```
425 ///
426 /// # Error
427 ///
428 /// This call will return an error if the user lacks permissions to make a new
429 /// directory at the provided path, or if the directory already exists.
430 pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
431     LocalIo::maybe_raise(|io| io.fs_mkdir(&path.to_c_str(), mode))
432 }
433
434 /// Remove an existing, empty directory
435 ///
436 /// # Example
437 ///
438 /// ```rust
439 /// # #[allow(unused_must_use)];
440 /// use std::io::fs;
441 ///
442 /// let p = Path::new("/some/dir");
443 /// fs::rmdir(&p);
444 /// ```
445 ///
446 /// # Error
447 ///
448 /// This call will return an error if the user lacks permissions to remove the
449 /// directory at the provided path, or if the directory isn't empty.
450 pub fn rmdir(path: &Path) -> IoResult<()> {
451     LocalIo::maybe_raise(|io| io.fs_rmdir(&path.to_c_str()))
452 }
453
454 /// Retrieve a vector containing all entries within a provided directory
455 ///
456 /// # Example
457 ///
458 /// ```rust
459 /// use std::io;
460 /// use std::io::fs;
461 ///
462 /// // one possible implementation of fs::walk_dir only visiting files
463 /// fn visit_dirs(dir: &Path, cb: |&Path|) -> io::IoResult<()> {
464 ///     if dir.is_dir() {
465 ///         let contents = try!(fs::readdir(dir));
466 ///         for entry in contents.iter() {
467 ///             if entry.is_dir() {
468 ///                 try!(visit_dirs(entry, |p| cb(p)));
469 ///             } else {
470 ///                 cb(entry);
471 ///             }
472 ///         }
473 ///         Ok(())
474 ///     } else {
475 ///         Err(io::standard_error(io::InvalidInput))
476 ///     }
477 /// }
478 /// ```
479 ///
480 /// # Error
481 ///
482 /// Will return an error if the provided `from` doesn't exist, the process lacks
483 /// permissions to view the contents or if the `path` points at a non-directory
484 /// file
485 pub fn readdir(path: &Path) -> IoResult<~[Path]> {
486     LocalIo::maybe_raise(|io| {
487         io.fs_readdir(&path.to_c_str(), 0)
488     })
489 }
490
491 /// Returns an iterator which will recursively walk the directory structure
492 /// rooted at `path`. The path given will not be iterated over, and this will
493 /// perform iteration in a top-down order.
494 pub fn walk_dir(path: &Path) -> IoResult<Directories> {
495     Ok(Directories { stack: try!(readdir(path)) })
496 }
497
498 /// An iterator which walks over a directory
499 pub struct Directories {
500     priv stack: ~[Path],
501 }
502
503 impl Iterator<Path> for Directories {
504     fn next(&mut self) -> Option<Path> {
505         match self.stack.shift() {
506             Some(path) => {
507                 if path.is_dir() {
508                     match readdir(&path) {
509                         Ok(dirs) => { self.stack.push_all_move(dirs); }
510                         Err(..) => {}
511                     }
512                 }
513                 Some(path)
514             }
515             None => None
516         }
517     }
518 }
519
520 /// Recursively create a directory and all of its parent components if they
521 /// are missing.
522 ///
523 /// # Error
524 ///
525 /// This function will return an `Err` value if an error happens, see
526 /// `fs::mkdir` for more information about error conditions and performance.
527 pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
528     // tjc: if directory exists but with different permissions,
529     // should we return false?
530     if path.is_dir() {
531         return Ok(())
532     }
533
534     let mut comps = path.components();
535     let mut curpath = path.root_path().unwrap_or(Path::new("."));
536
537     for c in comps {
538         curpath.push(c);
539
540         match mkdir(&curpath, mode) {
541             Err(mkdir_err) => {
542                 // already exists ?
543                 if try!(stat(&curpath)).kind != io::TypeDirectory {
544                     return Err(mkdir_err);
545                 }
546             }
547             Ok(()) => ()
548         }
549     }
550
551     Ok(())
552 }
553
554 /// Removes a directory at this path, after removing all its contents. Use
555 /// carefully!
556 ///
557 /// # Error
558 ///
559 /// This function will return an `Err` value if an error happens. See
560 /// `file::unlink` and `fs::readdir` for possible error conditions.
561 pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
562     let mut rm_stack = Vec::new();
563     rm_stack.push(path.clone());
564
565     while !rm_stack.is_empty() {
566         let children = try!(readdir(rm_stack.last().unwrap()));
567         let mut has_child_dir = false;
568
569         // delete all regular files in the way and push subdirs
570         // on the stack
571         for child in children.move_iter() {
572             // FIXME(#12795) we should use lstat in all cases
573             let child_type = match cfg!(windows) {
574                 true => try!(stat(&child)).kind,
575                 false => try!(lstat(&child)).kind
576             };
577
578             if child_type == io::TypeDirectory {
579                 rm_stack.push(child);
580                 has_child_dir = true;
581             } else {
582                 // we can carry on safely if the file is already gone
583                 // (eg: deleted by someone else since readdir)
584                 match unlink(&child) {
585                     Ok(()) => (),
586                     Err(ref e) if e.kind == io::FileNotFound => (),
587                     Err(e) => return Err(e)
588                 }
589             }
590         }
591
592         // if no subdir was found, let's pop and delete
593         if !has_child_dir {
594             match rmdir(&rm_stack.pop().unwrap()) {
595                 Ok(()) => (),
596                 Err(ref e) if e.kind == io::FileNotFound => (),
597                 Err(e) => return Err(e)
598             }
599         }
600     }
601
602     Ok(())
603 }
604
605 /// Changes the timestamps for a file's last modification and access time.
606 /// The file at the path specified will have its last access time set to
607 /// `atime` and its modification time set to `mtime`. The times specified should
608 /// be in milliseconds.
609 // FIXME(#10301) these arguments should not be u64
610 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
611     LocalIo::maybe_raise(|io| io.fs_utime(&path.to_c_str(), atime, mtime))
612 }
613
614 impl Reader for File {
615     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
616         match self.fd.read(buf) {
617             Ok(read) => {
618                 self.last_nread = read;
619                 match read {
620                     0 => Err(io::standard_error(io::EndOfFile)),
621                     _ => Ok(read as uint)
622                 }
623             },
624             Err(e) => Err(e),
625         }
626     }
627 }
628
629 impl Writer for File {
630     fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.fd.write(buf) }
631 }
632
633 impl Seek for File {
634     fn tell(&self) -> IoResult<u64> {
635         self.fd.tell()
636     }
637
638     fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
639         match self.fd.seek(pos, style) {
640             Ok(_) => {
641                 // successful seek resets EOF indicator
642                 self.last_nread = -1;
643                 Ok(())
644             }
645             Err(e) => Err(e),
646         }
647     }
648 }
649
650 impl path::Path {
651     /// Get information on the file, directory, etc at this path.
652     ///
653     /// Consult the `file::stat` documentation for more info.
654     ///
655     /// This call preserves identical runtime/error semantics with `file::stat`.
656     pub fn stat(&self) -> IoResult<FileStat> { stat(self) }
657
658     /// Boolean value indicator whether the underlying file exists on the local
659     /// filesystem. This will return true if the path points to either a
660     /// directory or a file.
661     ///
662     /// # Error
663     ///
664     /// Will not raise a condition
665     pub fn exists(&self) -> bool {
666         self.stat().is_ok()
667     }
668
669     /// Whether the underlying implementation (be it a file path, or something
670     /// else) points at a "regular file" on the FS. Will return false for paths
671     /// to non-existent locations or directories or other non-regular files
672     /// (named pipes, etc).
673     ///
674     /// # Error
675     ///
676     /// Will not raise a condition
677     pub fn is_file(&self) -> bool {
678         match self.stat() {
679             Ok(s) => s.kind == io::TypeFile,
680             Err(..) => false
681         }
682     }
683
684     /// Whether the underlying implementation (be it a file path,
685     /// or something else) is pointing at a directory in the underlying FS.
686     /// Will return false for paths to non-existent locations or if the item is
687     /// not a directory (eg files, named pipes, links, etc)
688     ///
689     /// # Error
690     ///
691     /// Will not raise a condition
692     pub fn is_dir(&self) -> bool {
693         match self.stat() {
694             Ok(s) => s.kind == io::TypeDirectory,
695             Err(..) => false
696         }
697     }
698 }
699
700 #[cfg(test)]
701 #[allow(unused_imports)]
702 mod test {
703     use prelude::*;
704     use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
705     use io;
706     use str;
707     use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive,
708                  mkdir_recursive, copy, unlink, stat, symlink, link,
709                  readlink, chmod, lstat, change_file_times};
710     use path::Path;
711     use io;
712     use ops::Drop;
713
714     macro_rules! check( ($e:expr) => (
715         match $e {
716             Ok(t) => t,
717             Err(e) => fail!("{} failed with: {}", stringify!($e), e),
718         }
719     ) )
720
721     struct TempDir(Path);
722
723     impl TempDir {
724         fn join(&self, path: &str) -> Path {
725             let TempDir(ref p) = *self;
726             p.join(path)
727         }
728
729         fn path<'a>(&'a self) -> &'a Path {
730             let TempDir(ref p) = *self;
731             p
732         }
733     }
734
735     impl Drop for TempDir {
736         fn drop(&mut self) {
737             // Gee, seeing how we're testing the fs module I sure hope that we
738             // at least implement this correctly!
739             let TempDir(ref p) = *self;
740             check!(io::fs::rmdir_recursive(p));
741         }
742     }
743
744     pub fn tmpdir() -> TempDir {
745         use os;
746         use rand;
747         let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>()));
748         check!(io::fs::mkdir(&ret, io::UserRWX));
749         TempDir(ret)
750     }
751
752     iotest!(fn file_test_io_smoke_test() {
753         let message = "it's alright. have a good time";
754         let tmpdir = tmpdir();
755         let filename = &tmpdir.join("file_rt_io_file_test.txt");
756         {
757             let mut write_stream = File::open_mode(filename, Open, ReadWrite);
758             check!(write_stream.write(message.as_bytes()));
759         }
760         {
761             let mut read_stream = File::open_mode(filename, Open, Read);
762             let mut read_buf = [0, .. 1028];
763             let read_str = match check!(read_stream.read(read_buf)) {
764                 -1|0 => fail!("shouldn't happen"),
765                 n => str::from_utf8(read_buf.slice_to(n).to_owned()).unwrap().to_owned()
766             };
767             assert_eq!(read_str, message.to_owned());
768         }
769         check!(unlink(filename));
770     })
771
772     iotest!(fn invalid_path_raises() {
773         let tmpdir = tmpdir();
774         let filename = &tmpdir.join("file_that_does_not_exist.txt");
775         let result = File::open_mode(filename, Open, Read);
776         assert!(result.is_err());
777     })
778
779     iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() {
780         let tmpdir = tmpdir();
781         let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
782         assert!(unlink(filename).is_err());
783     })
784
785     iotest!(fn file_test_io_non_positional_read() {
786         let message: &str = "ten-four";
787         let mut read_mem = [0, .. 8];
788         let tmpdir = tmpdir();
789         let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
790         {
791             let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
792             check!(rw_stream.write(message.as_bytes()));
793         }
794         {
795             let mut read_stream = File::open_mode(filename, Open, Read);
796             {
797                 let read_buf = read_mem.mut_slice(0, 4);
798                 check!(read_stream.read(read_buf));
799             }
800             {
801                 let read_buf = read_mem.mut_slice(4, 8);
802                 check!(read_stream.read(read_buf));
803             }
804         }
805         check!(unlink(filename));
806         let read_str = str::from_utf8(read_mem).unwrap();
807         assert_eq!(read_str, message);
808     })
809
810     iotest!(fn file_test_io_seek_and_tell_smoke_test() {
811         let message = "ten-four";
812         let mut read_mem = [0, .. 4];
813         let set_cursor = 4 as u64;
814         let mut tell_pos_pre_read;
815         let mut tell_pos_post_read;
816         let tmpdir = tmpdir();
817         let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
818         {
819             let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
820             check!(rw_stream.write(message.as_bytes()));
821         }
822         {
823             let mut read_stream = File::open_mode(filename, Open, Read);
824             check!(read_stream.seek(set_cursor as i64, SeekSet));
825             tell_pos_pre_read = check!(read_stream.tell());
826             check!(read_stream.read(read_mem));
827             tell_pos_post_read = check!(read_stream.tell());
828         }
829         check!(unlink(filename));
830         let read_str = str::from_utf8(read_mem).unwrap();
831         assert_eq!(read_str, message.slice(4, 8));
832         assert_eq!(tell_pos_pre_read, set_cursor);
833         assert_eq!(tell_pos_post_read, message.len() as u64);
834     })
835
836     iotest!(fn file_test_io_seek_and_write() {
837         let initial_msg =   "food-is-yummy";
838         let overwrite_msg =    "-the-bar!!";
839         let final_msg =     "foo-the-bar!!";
840         let seek_idx = 3;
841         let mut read_mem = [0, .. 13];
842         let tmpdir = tmpdir();
843         let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
844         {
845             let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
846             check!(rw_stream.write(initial_msg.as_bytes()));
847             check!(rw_stream.seek(seek_idx as i64, SeekSet));
848             check!(rw_stream.write(overwrite_msg.as_bytes()));
849         }
850         {
851             let mut read_stream = File::open_mode(filename, Open, Read);
852             check!(read_stream.read(read_mem));
853         }
854         check!(unlink(filename));
855         let read_str = str::from_utf8(read_mem).unwrap();
856         assert!(read_str == final_msg.to_owned());
857     })
858
859     iotest!(fn file_test_io_seek_shakedown() {
860         use std::str;          // 01234567890123
861         let initial_msg =   "qwer-asdf-zxcv";
862         let chunk_one: &str = "qwer";
863         let chunk_two: &str = "asdf";
864         let chunk_three: &str = "zxcv";
865         let mut read_mem = [0, .. 4];
866         let tmpdir = tmpdir();
867         let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
868         {
869             let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
870             check!(rw_stream.write(initial_msg.as_bytes()));
871         }
872         {
873             let mut read_stream = File::open_mode(filename, Open, Read);
874
875             check!(read_stream.seek(-4, SeekEnd));
876             check!(read_stream.read(read_mem));
877             assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_three);
878
879             check!(read_stream.seek(-9, SeekCur));
880             check!(read_stream.read(read_mem));
881             assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_two);
882
883             check!(read_stream.seek(0, SeekSet));
884             check!(read_stream.read(read_mem));
885             assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_one);
886         }
887         check!(unlink(filename));
888     })
889
890     iotest!(fn file_test_stat_is_correct_on_is_file() {
891         let tmpdir = tmpdir();
892         let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
893         {
894             let mut fs = File::open_mode(filename, Open, ReadWrite);
895             let msg = "hw";
896             fs.write(msg.as_bytes()).unwrap();
897         }
898         let stat_res = check!(stat(filename));
899         assert_eq!(stat_res.kind, io::TypeFile);
900         check!(unlink(filename));
901     })
902
903     iotest!(fn file_test_stat_is_correct_on_is_dir() {
904         let tmpdir = tmpdir();
905         let filename = &tmpdir.join("file_stat_correct_on_is_dir");
906         check!(mkdir(filename, io::UserRWX));
907         let stat_res = check!(filename.stat());
908         assert!(stat_res.kind == io::TypeDirectory);
909         check!(rmdir(filename));
910     })
911
912     iotest!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
913         let tmpdir = tmpdir();
914         let dir = &tmpdir.join("fileinfo_false_on_dir");
915         check!(mkdir(dir, io::UserRWX));
916         assert!(dir.is_file() == false);
917         check!(rmdir(dir));
918     })
919
920     iotest!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
921         let tmpdir = tmpdir();
922         let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
923         check!(File::create(file).write(bytes!("foo")));
924         assert!(file.exists());
925         check!(unlink(file));
926         assert!(!file.exists());
927     })
928
929     iotest!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
930         let tmpdir = tmpdir();
931         let dir = &tmpdir.join("before_and_after_dir");
932         assert!(!dir.exists());
933         check!(mkdir(dir, io::UserRWX));
934         assert!(dir.exists());
935         assert!(dir.is_dir());
936         check!(rmdir(dir));
937         assert!(!dir.exists());
938     })
939
940     iotest!(fn file_test_directoryinfo_readdir() {
941         use std::str;
942         let tmpdir = tmpdir();
943         let dir = &tmpdir.join("di_readdir");
944         check!(mkdir(dir, io::UserRWX));
945         let prefix = "foo";
946         for n in range(0,3) {
947             let f = dir.join(format!("{}.txt", n));
948             let mut w = check!(File::create(&f));
949             let msg_str = (prefix + n.to_str().to_owned()).to_owned();
950             let msg = msg_str.as_bytes();
951             check!(w.write(msg));
952         }
953         let files = check!(readdir(dir));
954         let mut mem = [0u8, .. 4];
955         for f in files.iter() {
956             {
957                 let n = f.filestem_str();
958                 check!(File::open(f).read(mem));
959                 let read_str = str::from_utf8(mem).unwrap();
960                 let expected = match n {
961                     None|Some("") => fail!("really shouldn't happen.."),
962                     Some(n) => prefix+n
963                 };
964                 assert_eq!(expected.as_slice(), read_str);
965             }
966             check!(unlink(f));
967         }
968         check!(rmdir(dir));
969     })
970
971     iotest!(fn recursive_mkdir() {
972         let tmpdir = tmpdir();
973         let dir = tmpdir.join("d1/d2");
974         check!(mkdir_recursive(&dir, io::UserRWX));
975         assert!(dir.is_dir())
976     })
977
978     iotest!(fn recursive_mkdir_slash() {
979         check!(mkdir_recursive(&Path::new("/"), io::UserRWX));
980     })
981
982     // FIXME(#12795) depends on lstat to work on windows
983     #[cfg(not(windows))]
984     iotest!(fn recursive_rmdir() {
985         let tmpdir = tmpdir();
986         let d1 = tmpdir.join("d1");
987         let dt = d1.join("t");
988         let dtt = dt.join("t");
989         let d2 = tmpdir.join("d2");
990         let canary = d2.join("do_not_delete");
991         check!(mkdir_recursive(&dtt, io::UserRWX));
992         check!(mkdir_recursive(&d2, io::UserRWX));
993         check!(File::create(&canary).write(bytes!("foo")));
994         check!(symlink(&d2, &dt.join("d2")));
995         check!(rmdir_recursive(&d1));
996
997         assert!(!d1.is_dir());
998         assert!(canary.exists());
999     })
1000
1001     iotest!(fn unicode_path_is_dir() {
1002         assert!(Path::new(".").is_dir());
1003         assert!(!Path::new("test/stdtest/fs.rs").is_dir());
1004
1005         let tmpdir = tmpdir();
1006
1007         let mut dirpath = tmpdir.path().clone();
1008         dirpath.push(format!("test-가一ー你好"));
1009         check!(mkdir(&dirpath, io::UserRWX));
1010         assert!(dirpath.is_dir());
1011
1012         let mut filepath = dirpath;
1013         filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs");
1014         check!(File::create(&filepath)); // ignore return; touch only
1015         assert!(!filepath.is_dir());
1016         assert!(filepath.exists());
1017     })
1018
1019     iotest!(fn unicode_path_exists() {
1020         assert!(Path::new(".").exists());
1021         assert!(!Path::new("test/nonexistent-bogus-path").exists());
1022
1023         let tmpdir = tmpdir();
1024         let unicode = tmpdir.path();
1025         let unicode = unicode.join(format!("test-각丁ー再见"));
1026         check!(mkdir(&unicode, io::UserRWX));
1027         assert!(unicode.exists());
1028         assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
1029     })
1030
1031     iotest!(fn copy_file_does_not_exist() {
1032         let from = Path::new("test/nonexistent-bogus-path");
1033         let to = Path::new("test/other-bogus-path");
1034         match copy(&from, &to) {
1035             Ok(..) => fail!(),
1036             Err(..) => {
1037                 assert!(!from.exists());
1038                 assert!(!to.exists());
1039             }
1040         }
1041     })
1042
1043     iotest!(fn copy_file_ok() {
1044         let tmpdir = tmpdir();
1045         let input = tmpdir.join("in.txt");
1046         let out = tmpdir.join("out.txt");
1047
1048         check!(File::create(&input).write(bytes!("hello")));
1049         check!(copy(&input, &out));
1050         let contents = check!(File::open(&out).read_to_end());
1051         assert_eq!(contents.as_slice(), bytes!("hello"));
1052
1053         assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm);
1054     })
1055
1056     iotest!(fn copy_file_dst_dir() {
1057         let tmpdir = tmpdir();
1058         let out = tmpdir.join("out");
1059
1060         check!(File::create(&out));
1061         match copy(&out, tmpdir.path()) {
1062             Ok(..) => fail!(), Err(..) => {}
1063         }
1064     })
1065
1066     iotest!(fn copy_file_dst_exists() {
1067         let tmpdir = tmpdir();
1068         let input = tmpdir.join("in");
1069         let output = tmpdir.join("out");
1070
1071         check!(File::create(&input).write("foo".as_bytes()));
1072         check!(File::create(&output).write("bar".as_bytes()));
1073         check!(copy(&input, &output));
1074
1075         assert_eq!(check!(File::open(&output).read_to_end()),
1076                    (bytes!("foo")).to_owned());
1077     })
1078
1079     iotest!(fn copy_file_src_dir() {
1080         let tmpdir = tmpdir();
1081         let out = tmpdir.join("out");
1082
1083         match copy(tmpdir.path(), &out) {
1084             Ok(..) => fail!(), Err(..) => {}
1085         }
1086         assert!(!out.exists());
1087     })
1088
1089     iotest!(fn copy_file_preserves_perm_bits() {
1090         let tmpdir = tmpdir();
1091         let input = tmpdir.join("in.txt");
1092         let out = tmpdir.join("out.txt");
1093
1094         check!(File::create(&input));
1095         check!(chmod(&input, io::UserRead));
1096         check!(copy(&input, &out));
1097         assert!(check!(out.stat()).perm & io::UserWrite == 0);
1098
1099         check!(chmod(&input, io::UserFile));
1100         check!(chmod(&out, io::UserFile));
1101     })
1102
1103     #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
1104     iotest!(fn symlinks_work() {
1105         let tmpdir = tmpdir();
1106         let input = tmpdir.join("in.txt");
1107         let out = tmpdir.join("out.txt");
1108
1109         check!(File::create(&input).write("foobar".as_bytes()));
1110         check!(symlink(&input, &out));
1111         if cfg!(not(windows)) {
1112             assert_eq!(check!(lstat(&out)).kind, io::TypeSymlink);
1113         }
1114         assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
1115         assert_eq!(check!(File::open(&out).read_to_end()),
1116                    (bytes!("foobar")).to_owned());
1117     })
1118
1119     #[cfg(not(windows))] // apparently windows doesn't like symlinks
1120     iotest!(fn symlink_noexist() {
1121         let tmpdir = tmpdir();
1122         // symlinks can point to things that don't exist
1123         check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar")));
1124         assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo"));
1125     })
1126
1127     iotest!(fn readlink_not_symlink() {
1128         let tmpdir = tmpdir();
1129         match readlink(tmpdir.path()) {
1130             Ok(..) => fail!("wanted a failure"),
1131             Err(..) => {}
1132         }
1133     })
1134
1135     iotest!(fn links_work() {
1136         let tmpdir = tmpdir();
1137         let input = tmpdir.join("in.txt");
1138         let out = tmpdir.join("out.txt");
1139
1140         check!(File::create(&input).write("foobar".as_bytes()));
1141         check!(link(&input, &out));
1142         if cfg!(not(windows)) {
1143             assert_eq!(check!(lstat(&out)).kind, io::TypeFile);
1144             assert_eq!(check!(stat(&out)).unstable.nlink, 2);
1145         }
1146         assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
1147         assert_eq!(check!(File::open(&out).read_to_end()),
1148                    (bytes!("foobar")).to_owned());
1149
1150         // can't link to yourself
1151         match link(&input, &input) {
1152             Ok(..) => fail!("wanted a failure"),
1153             Err(..) => {}
1154         }
1155         // can't link to something that doesn't exist
1156         match link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
1157             Ok(..) => fail!("wanted a failure"),
1158             Err(..) => {}
1159         }
1160     })
1161
1162     iotest!(fn chmod_works() {
1163         let tmpdir = tmpdir();
1164         let file = tmpdir.join("in.txt");
1165
1166         check!(File::create(&file));
1167         assert!(check!(stat(&file)).perm & io::UserWrite == io::UserWrite);
1168         check!(chmod(&file, io::UserRead));
1169         assert!(check!(stat(&file)).perm & io::UserWrite == 0);
1170
1171         match chmod(&tmpdir.join("foo"), io::UserRWX) {
1172             Ok(..) => fail!("wanted a failure"),
1173             Err(..) => {}
1174         }
1175
1176         check!(chmod(&file, io::UserFile));
1177     })
1178
1179     iotest!(fn sync_doesnt_kill_anything() {
1180         let tmpdir = tmpdir();
1181         let path = tmpdir.join("in.txt");
1182
1183         let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite));
1184         check!(file.fsync());
1185         check!(file.datasync());
1186         check!(file.write(bytes!("foo")));
1187         check!(file.fsync());
1188         check!(file.datasync());
1189         drop(file);
1190     })
1191
1192     iotest!(fn truncate_works() {
1193         let tmpdir = tmpdir();
1194         let path = tmpdir.join("in.txt");
1195
1196         let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite));
1197         check!(file.write(bytes!("foo")));
1198         check!(file.fsync());
1199
1200         // Do some simple things with truncation
1201         assert_eq!(check!(stat(&path)).size, 3);
1202         check!(file.truncate(10));
1203         assert_eq!(check!(stat(&path)).size, 10);
1204         check!(file.write(bytes!("bar")));
1205         check!(file.fsync());
1206         assert_eq!(check!(stat(&path)).size, 10);
1207         assert_eq!(check!(File::open(&path).read_to_end()),
1208                    (bytes!("foobar", 0, 0, 0, 0)).to_owned());
1209
1210         // Truncate to a smaller length, don't seek, and then write something.
1211         // Ensure that the intermediate zeroes are all filled in (we're seeked
1212         // past the end of the file).
1213         check!(file.truncate(2));
1214         assert_eq!(check!(stat(&path)).size, 2);
1215         check!(file.write(bytes!("wut")));
1216         check!(file.fsync());
1217         assert_eq!(check!(stat(&path)).size, 9);
1218         assert_eq!(check!(File::open(&path).read_to_end()),
1219                    (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned());
1220         drop(file);
1221     })
1222
1223     iotest!(fn open_flavors() {
1224         let tmpdir = tmpdir();
1225
1226         match File::open_mode(&tmpdir.join("a"), io::Open, io::Read) {
1227             Ok(..) => fail!(), Err(..) => {}
1228         }
1229         check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write));
1230         check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite));
1231         check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write));
1232         check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite));
1233         check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write));
1234         check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite));
1235
1236         check!(File::create(&tmpdir.join("h")).write("foo".as_bytes()));
1237         check!(File::open_mode(&tmpdir.join("h"), io::Open, io::Read));
1238         {
1239             let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Open,
1240                                                io::Read));
1241             match f.write("wut".as_bytes()) {
1242                 Ok(..) => fail!(), Err(..) => {}
1243             }
1244         }
1245         assert!(check!(stat(&tmpdir.join("h"))).size == 3,
1246                 "write/stat failed");
1247         {
1248             let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Append,
1249                                                io::Write));
1250             check!(f.write("bar".as_bytes()));
1251         }
1252         assert!(check!(stat(&tmpdir.join("h"))).size == 6,
1253                 "append didn't append");
1254         {
1255             let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Truncate,
1256                                                io::Write));
1257             check!(f.write("bar".as_bytes()));
1258         }
1259         assert!(check!(stat(&tmpdir.join("h"))).size == 3,
1260                 "truncate didn't truncate");
1261     })
1262
1263     #[test]
1264     fn utime() {
1265         let tmpdir = tmpdir();
1266         let path = tmpdir.join("a");
1267         check!(File::create(&path));
1268
1269         check!(change_file_times(&path, 1000, 2000));
1270         assert_eq!(check!(path.stat()).accessed, 1000);
1271         assert_eq!(check!(path.stat()).modified, 2000);
1272     }
1273
1274     #[test]
1275     fn utime_noexist() {
1276         let tmpdir = tmpdir();
1277
1278         match change_file_times(&tmpdir.join("a"), 100, 200) {
1279             Ok(..) => fail!(),
1280             Err(..) => {}
1281         }
1282     }
1283
1284     iotest!(fn binary_file() {
1285         use rand::{Rng, task_rng};
1286
1287         let mut bytes = [0, ..1024];
1288         task_rng().fill_bytes(bytes);
1289
1290         let tmpdir = tmpdir();
1291
1292         check!(File::create(&tmpdir.join("test")).write(bytes));
1293         let actual = check!(File::open(&tmpdir.join("test")).read_to_end());
1294         assert!(actual.as_slice() == bytes);
1295     })
1296 }