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