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