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.
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.
11 // ignore-lexer-test FIXME #15679
13 /*! Synchronous File I/O
15 This module provides a set of functions and traits for working
16 with regular files & directories on a filesystem.
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.
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.
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.
33 # #![allow(unused_must_use)]
34 use std::io::{File, fs};
36 let path = Path::new("foo.txt");
38 // create the file, whether it exists or not
39 let mut file = File::create(&path);
40 file.write(b"foobar");
43 // open the file in read-only mode
44 let mut file = File::open(&path);
47 println!("{}", path.stat().unwrap().size);
56 use collections::{Collection, MutableSeq};
57 use io::standard_error;
58 use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
59 use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
60 use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
61 use io::UpdateIoError;
66 use option::{Some, None, Option};
68 use path::{Path, GenericPath};
70 use result::{Err, Ok};
71 use rt::rtio::LocalIo;
73 use slice::ImmutableVector;
77 /// Unconstrained file access type that exposes read and write operations
79 /// Can be constructed via `File::open()`, `File::create()`, and
80 /// `File::open_mode()`.
84 /// This type will return errors as an `IoResult<T>` if operations are
85 /// attempted against it for which its underlying file descriptor was not
86 /// configured at creation time, via the `FileAccess` parameter to
87 /// `File::open_mode()`.
89 fd: Box<rtio::RtioFileStream + Send>,
95 /// Open a file at `path` in the mode specified by the `mode` and `access`
100 /// ```rust,should_fail
101 /// use std::io::{File, Open, ReadWrite};
103 /// let p = Path::new("/some/file/path.txt");
105 /// let file = match File::open_mode(&p, Open, ReadWrite) {
107 /// Err(e) => fail!("file error: {}", e),
109 /// // do some stuff with that file
111 /// // the file will be closed at the end of this block
114 /// `FileMode` and `FileAccess` provide information about the permissions
115 /// context in which a given stream is created. More information about them
116 /// can be found in `std::io`'s docs. If a file is opened with `Write`
117 /// or `ReadWrite` access, then it will be created if it does not already
120 /// Note that, with this function, a `File` is returned regardless of the
121 /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a
122 /// `File` opened as `Read` will return an error at runtime).
126 /// This function will return an error under a number of different
127 /// circumstances, to include but not limited to:
129 /// * Opening a file that does not exist with `Read` access.
130 /// * Attempting to open a file with a `FileAccess` that the user lacks
132 /// * Filesystem-level errors (full disk, etc)
133 pub fn open_mode(path: &Path,
135 access: FileAccess) -> IoResult<File> {
136 let rtio_mode = match mode {
138 Append => rtio::Append,
139 Truncate => rtio::Truncate,
141 let rtio_access = match access {
143 Write => rtio::Write,
144 ReadWrite => rtio::ReadWrite,
146 let err = LocalIo::maybe_raise(|io| {
147 io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| {
154 }).map_err(IoError::from_rtio_error);
155 err.update_err("couldn't open file", |e| {
156 format!("{}; path={}; mode={}; access={}", e, path.display(),
157 mode_string(mode), access_string(access))
161 /// Attempts to open a file in read-only mode. This function is equivalent to
162 /// `File::open_mode(path, Open, Read)`, and will raise all of the same
163 /// errors that `File::open_mode` does.
165 /// For more information, see the `File::open_mode` function.
170 /// use std::io::File;
172 /// let contents = File::open(&Path::new("foo.txt")).read_to_end();
174 pub fn open(path: &Path) -> IoResult<File> {
175 File::open_mode(path, Open, Read)
178 /// Attempts to create a file in write-only mode. This function is
179 /// equivalent to `File::open_mode(path, Truncate, Write)`, and will
180 /// raise all of the same errors that `File::open_mode` does.
182 /// For more information, see the `File::open_mode` function.
187 /// # #![allow(unused_must_use)]
188 /// use std::io::File;
190 /// let mut f = File::create(&Path::new("foo.txt"));
191 /// f.write(b"This is a sample file");
193 /// # ::std::io::fs::unlink(&Path::new("foo.txt"));
195 pub fn create(path: &Path) -> IoResult<File> {
196 File::open_mode(path, Truncate, Write)
197 .update_desc("couldn't create file")
200 /// Returns the original path which was used to open this file.
201 pub fn path<'a>(&'a self) -> &'a Path {
205 /// Synchronizes all modifications to this file to its permanent storage
206 /// device. This will flush any internal buffers necessary to perform this
208 pub fn fsync(&mut self) -> IoResult<()> {
209 let err = self.fd.fsync().map_err(IoError::from_rtio_error);
210 err.update_err("couldn't fsync file",
211 |e| format!("{}; path={}", e, self.path.display()))
214 /// This function is similar to `fsync`, except that it may not synchronize
215 /// file metadata to the filesystem. This is intended for use case which
216 /// must synchronize content, but don't need the metadata on disk. The goal
217 /// of this method is to reduce disk operations.
218 pub fn datasync(&mut self) -> IoResult<()> {
219 let err = self.fd.datasync().map_err(IoError::from_rtio_error);
220 err.update_err("couldn't datasync file",
221 |e| format!("{}; path={}", e, self.path.display()))
224 /// Either truncates or extends the underlying file, updating the size of
225 /// this file to become `size`. This is equivalent to unix's `truncate`
228 /// If the `size` is less than the current file's size, then the file will
229 /// be shrunk. If it is greater than the current file's size, then the file
230 /// will be extended to `size` and have all of the intermediate data filled
232 pub fn truncate(&mut self, size: i64) -> IoResult<()> {
233 let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
234 err.update_err("couldn't truncate file", |e| {
235 format!("{}; path={}; size={}", e, self.path.display(), size)
239 /// Tests whether this stream has reached EOF.
241 /// If true, then this file will no longer continue to return data via
243 pub fn eof(&self) -> bool {
247 /// Queries information about the underlying file.
248 pub fn stat(&mut self) -> IoResult<FileStat> {
249 let err = match self.fd.fstat() {
250 Ok(s) => Ok(from_rtio(s)),
251 Err(e) => Err(IoError::from_rtio_error(e)),
253 err.update_err("couldn't fstat file",
254 |e| format!("{}; path={}", e, self.path.display()))
258 /// Unlink a file from the underlying filesystem.
263 /// # #![allow(unused_must_use)]
266 /// let p = Path::new("/some/file/path.txt");
270 /// Note that, just because an unlink call was successful, it is not
271 /// guaranteed that a file is immediately deleted (e.g. depending on
272 /// platform, other open file descriptors may prevent immediate removal)
276 /// This function will return an error if `path` points to a directory, if the
277 /// user lacks permissions to remove the file, or if some other filesystem-level
279 pub fn unlink(path: &Path) -> IoResult<()> {
280 return match do_unlink(path) {
283 // On unix, a readonly file can be successfully removed. On windows,
284 // however, it cannot. To keep the two platforms in line with
285 // respect to their behavior, catch this case on windows, attempt to
286 // change it to read-write, and then remove the file.
287 if cfg!(windows) && e.kind == io::PermissionDenied {
288 let stat = match stat(path) {
290 Err(..) => return Err(e),
292 if stat.perm.intersects(io::UserWrite) { return Err(e) }
294 match chmod(path, stat.perm | io::UserWrite) {
295 Ok(()) => do_unlink(path),
297 // Try to put it back as we found it
298 let _ = chmod(path, stat.perm);
308 fn do_unlink(path: &Path) -> IoResult<()> {
309 let err = LocalIo::maybe_raise(|io| {
310 io.fs_unlink(&path.to_c_str())
311 }).map_err(IoError::from_rtio_error);
312 err.update_err("couldn't unlink path",
313 |e| format!("{}; path={}", e, path.display()))
317 /// Given a path, query the file system to get information about a file,
318 /// directory, etc. This function will traverse symlinks to query
319 /// information about the destination file.
326 /// let p = Path::new("/some/file/path.txt");
327 /// match fs::stat(&p) {
328 /// Ok(stat) => { /* ... */ }
329 /// Err(e) => { /* handle error */ }
335 /// This function will return an error if the user lacks the requisite permissions
336 /// to perform a `stat` call on the given `path` or if there is no entry in the
337 /// filesystem at the provided path.
338 pub fn stat(path: &Path) -> IoResult<FileStat> {
339 let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
340 Ok(s) => Ok(from_rtio(s)),
341 Err(e) => Err(IoError::from_rtio_error(e)),
343 err.update_err("couldn't stat path",
344 |e| format!("{}; path={}", e, path.display()))
347 /// Perform the same operation as the `stat` function, except that this
348 /// function does not traverse through symlinks. This will return
349 /// information about the symlink file instead of the file that it points
355 pub fn lstat(path: &Path) -> IoResult<FileStat> {
356 let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
357 Ok(s) => Ok(from_rtio(s)),
358 Err(e) => Err(IoError::from_rtio_error(e)),
360 err.update_err("couldn't lstat path",
361 |e| format!("{}; path={}", e, path.display()))
364 fn from_rtio(s: rtio::FileStat) -> FileStat {
366 type Mode = libc::c_int;
368 type Mode = libc::mode_t;
371 size, kind, perm, created, modified,
372 accessed, device, inode, rdev,
373 nlink, uid, gid, blksize, blocks, flags, gen
378 kind: match (kind as Mode) & libc::S_IFMT {
379 libc::S_IFREG => io::TypeFile,
380 libc::S_IFDIR => io::TypeDirectory,
381 libc::S_IFIFO => io::TypeNamedPipe,
382 libc::S_IFBLK => io::TypeBlockSpecial,
383 libc::S_IFLNK => io::TypeSymlink,
384 _ => io::TypeUnknown,
386 perm: FilePermission::from_bits_truncate(perm as u32),
390 unstable: UnstableFileStat {
405 /// Rename a file or directory to a new name.
410 /// # #![allow(unused_must_use)]
413 /// fs::rename(&Path::new("foo"), &Path::new("bar"));
418 /// This function will return an error if the provided `from` doesn't exist, if
419 /// the process lacks permissions to view the contents, or if some other
420 /// intermittent I/O error occurs.
421 pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
422 let err = LocalIo::maybe_raise(|io| {
423 io.fs_rename(&from.to_c_str(), &to.to_c_str())
424 }).map_err(IoError::from_rtio_error);
425 err.update_err("couldn't rename path", |e| {
426 format!("{}; from={}; to={}", e, from.display(), to.display())
430 /// Copies the contents of one file to another. This function will also
431 /// copy the permission bits of the original file to the destination file.
433 /// Note that if `from` and `to` both point to the same file, then the file
434 /// will likely get truncated by this operation.
439 /// # #![allow(unused_must_use)]
442 /// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt"));
447 /// This function will return an error in the following situations, but is not
448 /// limited to just these cases:
450 /// * The `from` path is not a file
451 /// * The `from` file does not exist
452 /// * The current process does not have the permission rights to access
453 /// `from` or write `to`
455 /// Note that this copy is not atomic in that once the destination is
456 /// ensured to not exist, there is nothing preventing the destination from
457 /// being created and then destroyed by this operation.
458 pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
459 fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
460 result.update_err("couldn't copy path",
461 |e| format!("{}; from={}; to={}", e, from.display(), to.display()))
465 return update_err(Err(IoError {
466 kind: io::MismatchedFileTypeForOperation,
467 desc: "the source path is not an existing file",
472 let mut reader = try!(File::open(from));
473 let mut writer = try!(File::create(to));
474 let mut buf = [0, ..io::DEFAULT_BUF_SIZE];
477 let amt = match reader.read(buf) {
479 Err(ref e) if e.kind == io::EndOfFile => { break }
480 Err(e) => return update_err(Err(e), from, to)
482 try!(writer.write(buf.slice_to(amt)));
485 chmod(to, try!(update_err(from.stat(), from, to)).perm)
488 /// Changes the permission mode bits found on a file or a directory. This
489 /// function takes a mask from the `io` module
494 /// # #![allow(unused_must_use)]
498 /// fs::chmod(&Path::new("file.txt"), io::UserFile);
499 /// fs::chmod(&Path::new("file.txt"), io::UserRead | io::UserWrite);
500 /// fs::chmod(&Path::new("dir"), io::UserDir);
501 /// fs::chmod(&Path::new("file.exe"), io::UserExec);
506 /// This function will return an error if the provided `path` doesn't exist, if
507 /// the process lacks permissions to change the attributes of the file, or if
508 /// some other I/O error is encountered.
509 pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
510 let err = LocalIo::maybe_raise(|io| {
511 io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
512 }).map_err(IoError::from_rtio_error);
513 err.update_err("couldn't chmod path", |e| {
514 format!("{}; path={}; mode={}", e, path.display(), mode)
518 /// Change the user and group owners of a file at the specified path.
519 pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
520 let err = LocalIo::maybe_raise(|io| {
521 io.fs_chown(&path.to_c_str(), uid, gid)
522 }).map_err(IoError::from_rtio_error);
523 err.update_err("couldn't chown path", |e| {
524 format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
528 /// Creates a new hard link on the filesystem. The `dst` path will be a
529 /// link pointing to the `src` path. Note that systems often require these
530 /// two paths to both be located on the same filesystem.
531 pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
532 let err = LocalIo::maybe_raise(|io| {
533 io.fs_link(&src.to_c_str(), &dst.to_c_str())
534 }).map_err(IoError::from_rtio_error);
535 err.update_err("couldn't link path", |e| {
536 format!("{}; src={}; dest={}", e, src.display(), dst.display())
540 /// Creates a new symbolic link on the filesystem. The `dst` path will be a
541 /// symlink pointing to the `src` path.
542 pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
543 let err = LocalIo::maybe_raise(|io| {
544 io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
545 }).map_err(IoError::from_rtio_error);
546 err.update_err("couldn't symlink path", |e| {
547 format!("{}; src={}; dest={}", e, src.display(), dst.display())
551 /// Reads a symlink, returning the file that the symlink points to.
555 /// This function will return an error on failure. Failure conditions include
556 /// reading a file that does not exist or reading a file which is not a symlink.
557 pub fn readlink(path: &Path) -> IoResult<Path> {
558 let err = LocalIo::maybe_raise(|io| {
559 Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
560 }).map_err(IoError::from_rtio_error);
561 err.update_err("couldn't resolve symlink for path",
562 |e| format!("{}; path={}", e, path.display()))
565 /// Create a new, empty directory at the provided path
570 /// # #![allow(unused_must_use)]
574 /// let p = Path::new("/some/dir");
575 /// fs::mkdir(&p, io::UserRWX);
580 /// This function will return an error if the user lacks permissions to make a
581 /// new directory at the provided `path`, or if the directory already exists.
582 pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
583 let err = LocalIo::maybe_raise(|io| {
584 io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
585 }).map_err(IoError::from_rtio_error);
586 err.update_err("couldn't create directory", |e| {
587 format!("{}; path={}; mode={}", e, path.display(), mode)
591 /// Remove an existing, empty directory
596 /// # #![allow(unused_must_use)]
599 /// let p = Path::new("/some/dir");
605 /// This function will return an error if the user lacks permissions to remove
606 /// the directory at the provided `path`, or if the directory isn't empty.
607 pub fn rmdir(path: &Path) -> IoResult<()> {
608 let err = LocalIo::maybe_raise(|io| {
609 io.fs_rmdir(&path.to_c_str())
610 }).map_err(IoError::from_rtio_error);
611 err.update_err("couldn't remove directory",
612 |e| format!("{}; path={}", e, path.display()))
615 /// Retrieve a vector containing all entries within a provided directory
623 /// // one possible implementation of fs::walk_dir only visiting files
624 /// fn visit_dirs(dir: &Path, cb: |&Path|) -> io::IoResult<()> {
625 /// if dir.is_dir() {
626 /// let contents = try!(fs::readdir(dir));
627 /// for entry in contents.iter() {
628 /// if entry.is_dir() {
629 /// try!(visit_dirs(entry, |p| cb(p)));
636 /// Err(io::standard_error(io::InvalidInput))
643 /// This function will return an error if the provided `path` doesn't exist, if
644 /// the process lacks permissions to view the contents or if the `path` points
645 /// at a non-directory file
646 pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
647 let err = LocalIo::maybe_raise(|io| {
648 Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).move_iter().map(|a| {
651 }).map_err(IoError::from_rtio_error);
652 err.update_err("couldn't read directory",
653 |e| format!("{}; path={}", e, path.display()))
656 /// Returns an iterator which will recursively walk the directory structure
657 /// rooted at `path`. The path given will not be iterated over, and this will
658 /// perform iteration in some top-down order. The contents of unreadable
659 /// subdirectories are ignored.
660 pub fn walk_dir(path: &Path) -> IoResult<Directories> {
662 stack: try!(readdir(path).update_err("couldn't walk directory",
663 |e| format!("{}; path={}",
668 /// An iterator which walks over a directory
669 pub struct Directories {
673 impl Iterator<Path> for Directories {
674 fn next(&mut self) -> Option<Path> {
675 match self.stack.pop() {
678 let result = readdir(&path)
679 .update_err("couldn't advance Directories iterator",
680 |e| format!("{}; path={}",
684 Ok(dirs) => { self.stack.push_all_move(dirs); }
695 /// Recursively create a directory and all of its parent components if they
701 pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
702 // tjc: if directory exists but with different permissions,
703 // should we return false?
708 let mut comps = path.components();
709 let mut curpath = path.root_path().unwrap_or(Path::new("."));
714 let result = mkdir(&curpath, mode)
715 .update_err("couldn't recursively mkdir",
716 |e| format!("{}; path={}", e, path.display()));
721 if try!(stat(&curpath)).kind != io::TypeDirectory {
722 return Err(mkdir_err);
732 /// Removes a directory at this path, after removing all its contents. Use
737 /// See `file::unlink` and `fs::readdir`
738 pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
739 let mut rm_stack = Vec::new();
740 rm_stack.push(path.clone());
742 fn rmdir_failed(err: &IoError, path: &Path) -> String {
743 format!("rmdir_recursive failed; path={}; cause={}",
747 fn update_err<T>(err: IoResult<T>, path: &Path) -> IoResult<T> {
748 err.update_err("couldn't recursively rmdir",
749 |e| rmdir_failed(e, path))
752 while !rm_stack.is_empty() {
753 let children = try!(readdir(rm_stack.last().unwrap())
754 .update_detail(|e| rmdir_failed(e, path)));
756 let mut has_child_dir = false;
758 // delete all regular files in the way and push subdirs
760 for child in children.move_iter() {
761 // FIXME(#12795) we should use lstat in all cases
762 let child_type = match cfg!(windows) {
763 true => try!(update_err(stat(&child), path)),
764 false => try!(update_err(lstat(&child), path))
767 if child_type.kind == io::TypeDirectory {
768 rm_stack.push(child);
769 has_child_dir = true;
771 // we can carry on safely if the file is already gone
772 // (eg: deleted by someone else since readdir)
773 match update_err(unlink(&child), path) {
775 Err(ref e) if e.kind == io::FileNotFound => (),
776 Err(e) => return Err(e)
781 // if no subdir was found, let's pop and delete
783 let result = update_err(rmdir(&rm_stack.pop().unwrap()), path);
786 Err(ref e) if e.kind == io::FileNotFound => (),
787 Err(e) => return Err(e)
795 /// Changes the timestamps for a file's last modification and access time.
796 /// The file at the path specified will have its last access time set to
797 /// `atime` and its modification time set to `mtime`. The times specified should
798 /// be in milliseconds.
799 // FIXME(#10301) these arguments should not be u64
800 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
801 let err = LocalIo::maybe_raise(|io| {
802 io.fs_utime(&path.to_c_str(), atime, mtime)
803 }).map_err(IoError::from_rtio_error);
804 err.update_err("couldn't change_file_times",
805 |e| format!("{}; path={}", e, path.display()))
808 impl Reader for File {
809 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
810 fn update_err<T>(result: IoResult<T>, file: &File) -> IoResult<T> {
811 result.update_err("couldn't read file",
812 |e| format!("{}; path={}",
813 e, file.path.display()))
816 let result = update_err(self.fd.read(buf)
817 .map_err(IoError::from_rtio_error), self);
821 self.last_nread = read;
823 0 => update_err(Err(standard_error(io::EndOfFile)), self),
824 _ => Ok(read as uint)
832 impl Writer for File {
833 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
834 let err = self.fd.write(buf).map_err(IoError::from_rtio_error);
835 err.update_err("couldn't write to file",
836 |e| format!("{}; path={}", e, self.path.display()))
841 fn tell(&self) -> IoResult<u64> {
842 let err = self.fd.tell().map_err(IoError::from_rtio_error);
843 err.update_err("couldn't retrieve file cursor (`tell`)",
844 |e| format!("{}; path={}", e, self.path.display()))
847 fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
848 let style = match style {
849 SeekSet => rtio::SeekSet,
850 SeekCur => rtio::SeekCur,
851 SeekEnd => rtio::SeekEnd,
853 let err = match self.fd.seek(pos, style) {
855 // successful seek resets EOF indicator
856 self.last_nread = -1;
859 Err(e) => Err(IoError::from_rtio_error(e)),
861 err.update_err("couldn't seek in file",
862 |e| format!("{}; path={}", e, self.path.display()))
867 /// Get information on the file, directory, etc at this path.
869 /// Consult the `fs::stat` documentation for more info.
871 /// This call preserves identical runtime/error semantics with `file::stat`.
872 pub fn stat(&self) -> IoResult<FileStat> { stat(self) }
874 /// Get information on the file, directory, etc at this path, not following
877 /// Consult the `fs::lstat` documentation for more info.
879 /// This call preserves identical runtime/error semantics with `file::lstat`.
880 pub fn lstat(&self) -> IoResult<FileStat> { lstat(self) }
882 /// Boolean value indicator whether the underlying file exists on the local
883 /// filesystem. Returns false in exactly the cases where `fs::stat` fails.
884 pub fn exists(&self) -> bool {
888 /// Whether the underlying implementation (be it a file path, or something
889 /// else) points at a "regular file" on the FS. Will return false for paths
890 /// to non-existent locations or directories or other non-regular files
891 /// (named pipes, etc). Follows links when making this determination.
892 pub fn is_file(&self) -> bool {
894 Ok(s) => s.kind == io::TypeFile,
899 /// Whether the underlying implementation (be it a file path, or something
900 /// else) is pointing at a directory in the underlying FS. Will return
901 /// false for paths to non-existent locations or if the item is not a
902 /// directory (eg files, named pipes, etc). Follows links when making this
904 pub fn is_dir(&self) -> bool {
906 Ok(s) => s.kind == io::TypeDirectory,
912 fn mode_string(mode: FileMode) -> &'static str {
914 super::Open => "open",
915 super::Append => "append",
916 super::Truncate => "truncate"
920 fn access_string(access: FileAccess) -> &'static str {
922 super::Read => "read",
923 super::Write => "write",
924 super::ReadWrite => "readwrite"
929 #[allow(unused_imports)]
932 use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
935 use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive,
936 mkdir_recursive, copy, unlink, stat, symlink, link,
937 readlink, chmod, lstat, change_file_times};
943 macro_rules! check( ($e:expr) => (
946 Err(e) => fail!("{} failed with: {}", stringify!($e), e),
950 macro_rules! error( ($e:expr, $s:expr) => (
952 Ok(val) => fail!("Should have been an error, was {:?}", val),
953 Err(ref err) => assert!(err.to_string().as_slice().contains($s.as_slice()),
954 format!("`{}` did not contain `{}`", err, $s))
958 pub struct TempDir(Path);
961 fn join(&self, path: &str) -> Path {
962 let TempDir(ref p) = *self;
966 fn path<'a>(&'a self) -> &'a Path {
967 let TempDir(ref p) = *self;
972 impl Drop for TempDir {
974 // Gee, seeing how we're testing the fs module I sure hope that we
975 // at least implement this correctly!
976 let TempDir(ref p) = *self;
977 check!(io::fs::rmdir_recursive(p));
981 pub fn tmpdir() -> TempDir {
984 let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>()));
985 check!(io::fs::mkdir(&ret, io::UserRWX));
989 iotest!(fn file_test_io_smoke_test() {
990 let message = "it's alright. have a good time";
991 let tmpdir = tmpdir();
992 let filename = &tmpdir.join("file_rt_io_file_test.txt");
994 let mut write_stream = File::open_mode(filename, Open, ReadWrite);
995 check!(write_stream.write(message.as_bytes()));
998 let mut read_stream = File::open_mode(filename, Open, Read);
999 let mut read_buf = [0, .. 1028];
1000 let read_str = match check!(read_stream.read(read_buf)) {
1001 -1|0 => fail!("shouldn't happen"),
1002 n => str::from_utf8(read_buf.slice_to(n)).unwrap().to_string()
1004 assert_eq!(read_str.as_slice(), message);
1006 check!(unlink(filename));
1009 iotest!(fn invalid_path_raises() {
1010 let tmpdir = tmpdir();
1011 let filename = &tmpdir.join("file_that_does_not_exist.txt");
1012 let result = File::open_mode(filename, Open, Read);
1014 error!(result, "couldn't open file");
1016 error!(result, "no such file or directory");
1018 error!(result, format!("path={}; mode=open; access=read", filename.display()));
1021 iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() {
1022 let tmpdir = tmpdir();
1023 let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
1025 let result = unlink(filename);
1027 error!(result, "couldn't unlink path");
1029 error!(result, "no such file or directory");
1031 error!(result, format!("path={}", filename.display()));
1034 iotest!(fn file_test_io_non_positional_read() {
1035 let message: &str = "ten-four";
1036 let mut read_mem = [0, .. 8];
1037 let tmpdir = tmpdir();
1038 let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
1040 let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
1041 check!(rw_stream.write(message.as_bytes()));
1044 let mut read_stream = File::open_mode(filename, Open, Read);
1046 let read_buf = read_mem.mut_slice(0, 4);
1047 check!(read_stream.read(read_buf));
1050 let read_buf = read_mem.mut_slice(4, 8);
1051 check!(read_stream.read(read_buf));
1054 check!(unlink(filename));
1055 let read_str = str::from_utf8(read_mem).unwrap();
1056 assert_eq!(read_str, message);
1059 iotest!(fn file_test_io_seek_and_tell_smoke_test() {
1060 let message = "ten-four";
1061 let mut read_mem = [0, .. 4];
1062 let set_cursor = 4 as u64;
1063 let mut tell_pos_pre_read;
1064 let mut tell_pos_post_read;
1065 let tmpdir = tmpdir();
1066 let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
1068 let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
1069 check!(rw_stream.write(message.as_bytes()));
1072 let mut read_stream = File::open_mode(filename, Open, Read);
1073 check!(read_stream.seek(set_cursor as i64, SeekSet));
1074 tell_pos_pre_read = check!(read_stream.tell());
1075 check!(read_stream.read(read_mem));
1076 tell_pos_post_read = check!(read_stream.tell());
1078 check!(unlink(filename));
1079 let read_str = str::from_utf8(read_mem).unwrap();
1080 assert_eq!(read_str, message.slice(4, 8));
1081 assert_eq!(tell_pos_pre_read, set_cursor);
1082 assert_eq!(tell_pos_post_read, message.len() as u64);
1085 iotest!(fn file_test_io_seek_and_write() {
1086 let initial_msg = "food-is-yummy";
1087 let overwrite_msg = "-the-bar!!";
1088 let final_msg = "foo-the-bar!!";
1090 let mut read_mem = [0, .. 13];
1091 let tmpdir = tmpdir();
1092 let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
1094 let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
1095 check!(rw_stream.write(initial_msg.as_bytes()));
1096 check!(rw_stream.seek(seek_idx as i64, SeekSet));
1097 check!(rw_stream.write(overwrite_msg.as_bytes()));
1100 let mut read_stream = File::open_mode(filename, Open, Read);
1101 check!(read_stream.read(read_mem));
1103 check!(unlink(filename));
1104 let read_str = str::from_utf8(read_mem).unwrap();
1105 assert!(read_str.as_slice() == final_msg.as_slice());
1108 iotest!(fn file_test_io_seek_shakedown() {
1109 use str; // 01234567890123
1110 let initial_msg = "qwer-asdf-zxcv";
1111 let chunk_one: &str = "qwer";
1112 let chunk_two: &str = "asdf";
1113 let chunk_three: &str = "zxcv";
1114 let mut read_mem = [0, .. 4];
1115 let tmpdir = tmpdir();
1116 let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
1118 let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
1119 check!(rw_stream.write(initial_msg.as_bytes()));
1122 let mut read_stream = File::open_mode(filename, Open, Read);
1124 check!(read_stream.seek(-4, SeekEnd));
1125 check!(read_stream.read(read_mem));
1126 assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_three);
1128 check!(read_stream.seek(-9, SeekCur));
1129 check!(read_stream.read(read_mem));
1130 assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_two);
1132 check!(read_stream.seek(0, SeekSet));
1133 check!(read_stream.read(read_mem));
1134 assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_one);
1136 check!(unlink(filename));
1139 iotest!(fn file_test_stat_is_correct_on_is_file() {
1140 let tmpdir = tmpdir();
1141 let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
1143 let mut fs = check!(File::open_mode(filename, Open, ReadWrite));
1145 fs.write(msg.as_bytes()).unwrap();
1147 let fstat_res = check!(fs.stat());
1148 assert_eq!(fstat_res.kind, io::TypeFile);
1150 let stat_res_fn = check!(stat(filename));
1151 assert_eq!(stat_res_fn.kind, io::TypeFile);
1152 let stat_res_meth = check!(filename.stat());
1153 assert_eq!(stat_res_meth.kind, io::TypeFile);
1154 check!(unlink(filename));
1157 iotest!(fn file_test_stat_is_correct_on_is_dir() {
1158 let tmpdir = tmpdir();
1159 let filename = &tmpdir.join("file_stat_correct_on_is_dir");
1160 check!(mkdir(filename, io::UserRWX));
1161 let stat_res_fn = check!(stat(filename));
1162 assert!(stat_res_fn.kind == io::TypeDirectory);
1163 let stat_res_meth = check!(filename.stat());
1164 assert!(stat_res_meth.kind == io::TypeDirectory);
1165 check!(rmdir(filename));
1168 iotest!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
1169 let tmpdir = tmpdir();
1170 let dir = &tmpdir.join("fileinfo_false_on_dir");
1171 check!(mkdir(dir, io::UserRWX));
1172 assert!(dir.is_file() == false);
1176 iotest!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
1177 let tmpdir = tmpdir();
1178 let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
1179 check!(File::create(file).write(b"foo"));
1180 assert!(file.exists());
1181 check!(unlink(file));
1182 assert!(!file.exists());
1185 iotest!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
1186 let tmpdir = tmpdir();
1187 let dir = &tmpdir.join("before_and_after_dir");
1188 assert!(!dir.exists());
1189 check!(mkdir(dir, io::UserRWX));
1190 assert!(dir.exists());
1191 assert!(dir.is_dir());
1193 assert!(!dir.exists());
1196 iotest!(fn file_test_directoryinfo_readdir() {
1198 let tmpdir = tmpdir();
1199 let dir = &tmpdir.join("di_readdir");
1200 check!(mkdir(dir, io::UserRWX));
1202 for n in range(0i,3) {
1203 let f = dir.join(format!("{}.txt", n));
1204 let mut w = check!(File::create(&f));
1205 let msg_str = format!("{}{}", prefix, n.to_string());
1206 let msg = msg_str.as_slice().as_bytes();
1207 check!(w.write(msg));
1209 let files = check!(readdir(dir));
1210 let mut mem = [0u8, .. 4];
1211 for f in files.iter() {
1213 let n = f.filestem_str();
1214 check!(File::open(f).read(mem));
1215 let read_str = str::from_utf8(mem).unwrap();
1216 let expected = match n {
1217 None|Some("") => fail!("really shouldn't happen.."),
1218 Some(n) => format!("{}{}", prefix, n),
1220 assert_eq!(expected.as_slice(), read_str);
1227 iotest!(fn file_test_walk_dir() {
1228 let tmpdir = tmpdir();
1229 let dir = &tmpdir.join("walk_dir");
1230 check!(mkdir(dir, io::UserRWX));
1232 let dir1 = &dir.join("01/02/03");
1233 check!(mkdir_recursive(dir1, io::UserRWX));
1234 check!(File::create(&dir1.join("04")));
1236 let dir2 = &dir.join("11/12/13");
1237 check!(mkdir_recursive(dir2, io::UserRWX));
1238 check!(File::create(&dir2.join("14")));
1240 let mut files = check!(walk_dir(dir));
1241 let mut cur = [0u8, .. 2];
1243 let stem = f.filestem_str().unwrap();
1244 let root = stem.as_bytes()[0] - ('0' as u8);
1245 let name = stem.as_bytes()[1] - ('0' as u8);
1246 assert!(cur[root as uint] < name);
1247 cur[root as uint] = name;
1250 check!(rmdir_recursive(dir));
1253 iotest!(fn recursive_mkdir() {
1254 let tmpdir = tmpdir();
1255 let dir = tmpdir.join("d1/d2");
1256 check!(mkdir_recursive(&dir, io::UserRWX));
1257 assert!(dir.is_dir())
1260 iotest!(fn recursive_mkdir_failure() {
1261 let tmpdir = tmpdir();
1262 let dir = tmpdir.join("d1");
1263 let file = dir.join("f1");
1265 check!(mkdir_recursive(&dir, io::UserRWX));
1266 check!(File::create(&file));
1268 let result = mkdir_recursive(&file, io::UserRWX);
1270 error!(result, "couldn't recursively mkdir");
1271 error!(result, "couldn't create directory");
1272 error!(result, "mode=FilePermission { bits: 448 }");
1273 error!(result, format!("path={}", file.display()));
1276 iotest!(fn recursive_mkdir_slash() {
1277 check!(mkdir_recursive(&Path::new("/"), io::UserRWX));
1280 // FIXME(#12795) depends on lstat to work on windows
1281 #[cfg(not(windows))]
1282 iotest!(fn recursive_rmdir() {
1283 let tmpdir = tmpdir();
1284 let d1 = tmpdir.join("d1");
1285 let dt = d1.join("t");
1286 let dtt = dt.join("t");
1287 let d2 = tmpdir.join("d2");
1288 let canary = d2.join("do_not_delete");
1289 check!(mkdir_recursive(&dtt, io::UserRWX));
1290 check!(mkdir_recursive(&d2, io::UserRWX));
1291 check!(File::create(&canary).write(b"foo"));
1292 check!(symlink(&d2, &dt.join("d2")));
1293 check!(rmdir_recursive(&d1));
1295 assert!(!d1.is_dir());
1296 assert!(canary.exists());
1299 iotest!(fn unicode_path_is_dir() {
1300 assert!(Path::new(".").is_dir());
1301 assert!(!Path::new("test/stdtest/fs.rs").is_dir());
1303 let tmpdir = tmpdir();
1305 let mut dirpath = tmpdir.path().clone();
1306 dirpath.push(format!("test-가一ー你好"));
1307 check!(mkdir(&dirpath, io::UserRWX));
1308 assert!(dirpath.is_dir());
1310 let mut filepath = dirpath;
1311 filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs");
1312 check!(File::create(&filepath)); // ignore return; touch only
1313 assert!(!filepath.is_dir());
1314 assert!(filepath.exists());
1317 iotest!(fn unicode_path_exists() {
1318 assert!(Path::new(".").exists());
1319 assert!(!Path::new("test/nonexistent-bogus-path").exists());
1321 let tmpdir = tmpdir();
1322 let unicode = tmpdir.path();
1323 let unicode = unicode.join(format!("test-각丁ー再见"));
1324 check!(mkdir(&unicode, io::UserRWX));
1325 assert!(unicode.exists());
1326 assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
1329 iotest!(fn copy_file_does_not_exist() {
1330 let from = Path::new("test/nonexistent-bogus-path");
1331 let to = Path::new("test/other-bogus-path");
1333 error!(copy(&from, &to),
1334 format!("couldn't copy path (the source path is not an \
1335 existing file; from={}; to={})",
1336 from.display(), to.display()));
1338 match copy(&from, &to) {
1341 assert!(!from.exists());
1342 assert!(!to.exists());
1347 iotest!(fn copy_file_ok() {
1348 let tmpdir = tmpdir();
1349 let input = tmpdir.join("in.txt");
1350 let out = tmpdir.join("out.txt");
1352 check!(File::create(&input).write(b"hello"));
1353 check!(copy(&input, &out));
1354 let contents = check!(File::open(&out).read_to_end());
1355 assert_eq!(contents.as_slice(), b"hello");
1357 assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm);
1360 iotest!(fn copy_file_dst_dir() {
1361 let tmpdir = tmpdir();
1362 let out = tmpdir.join("out");
1364 check!(File::create(&out));
1365 match copy(&out, tmpdir.path()) {
1366 Ok(..) => fail!(), Err(..) => {}
1370 iotest!(fn copy_file_dst_exists() {
1371 let tmpdir = tmpdir();
1372 let input = tmpdir.join("in");
1373 let output = tmpdir.join("out");
1375 check!(File::create(&input).write("foo".as_bytes()));
1376 check!(File::create(&output).write("bar".as_bytes()));
1377 check!(copy(&input, &output));
1379 assert_eq!(check!(File::open(&output).read_to_end()),
1380 (Vec::from_slice(b"foo")));
1383 iotest!(fn copy_file_src_dir() {
1384 let tmpdir = tmpdir();
1385 let out = tmpdir.join("out");
1387 match copy(tmpdir.path(), &out) {
1388 Ok(..) => fail!(), Err(..) => {}
1390 assert!(!out.exists());
1393 iotest!(fn copy_file_preserves_perm_bits() {
1394 let tmpdir = tmpdir();
1395 let input = tmpdir.join("in.txt");
1396 let out = tmpdir.join("out.txt");
1398 check!(File::create(&input));
1399 check!(chmod(&input, io::UserRead));
1400 check!(copy(&input, &out));
1401 assert!(!check!(out.stat()).perm.intersects(io::UserWrite));
1403 check!(chmod(&input, io::UserFile));
1404 check!(chmod(&out, io::UserFile));
1407 #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
1408 iotest!(fn symlinks_work() {
1409 let tmpdir = tmpdir();
1410 let input = tmpdir.join("in.txt");
1411 let out = tmpdir.join("out.txt");
1413 check!(File::create(&input).write("foobar".as_bytes()));
1414 check!(symlink(&input, &out));
1415 if cfg!(not(windows)) {
1416 assert_eq!(check!(lstat(&out)).kind, io::TypeSymlink);
1417 assert_eq!(check!(out.lstat()).kind, io::TypeSymlink);
1419 assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
1420 assert_eq!(check!(File::open(&out).read_to_end()),
1421 (Vec::from_slice(b"foobar")));
1424 #[cfg(not(windows))] // apparently windows doesn't like symlinks
1425 iotest!(fn symlink_noexist() {
1426 let tmpdir = tmpdir();
1427 // symlinks can point to things that don't exist
1428 check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar")));
1429 assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo"));
1432 iotest!(fn readlink_not_symlink() {
1433 let tmpdir = tmpdir();
1434 match readlink(tmpdir.path()) {
1435 Ok(..) => fail!("wanted a failure"),
1440 iotest!(fn links_work() {
1441 let tmpdir = tmpdir();
1442 let input = tmpdir.join("in.txt");
1443 let out = tmpdir.join("out.txt");
1445 check!(File::create(&input).write("foobar".as_bytes()));
1446 check!(link(&input, &out));
1447 if cfg!(not(windows)) {
1448 assert_eq!(check!(lstat(&out)).kind, io::TypeFile);
1449 assert_eq!(check!(out.lstat()).kind, io::TypeFile);
1450 assert_eq!(check!(stat(&out)).unstable.nlink, 2);
1451 assert_eq!(check!(out.stat()).unstable.nlink, 2);
1453 assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
1454 assert_eq!(check!(stat(&out)).size, check!(input.stat()).size);
1455 assert_eq!(check!(File::open(&out).read_to_end()),
1456 (Vec::from_slice(b"foobar")));
1458 // can't link to yourself
1459 match link(&input, &input) {
1460 Ok(..) => fail!("wanted a failure"),
1463 // can't link to something that doesn't exist
1464 match link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
1465 Ok(..) => fail!("wanted a failure"),
1470 iotest!(fn chmod_works() {
1471 let tmpdir = tmpdir();
1472 let file = tmpdir.join("in.txt");
1474 check!(File::create(&file));
1475 assert!(check!(stat(&file)).perm.contains(io::UserWrite));
1476 check!(chmod(&file, io::UserRead));
1477 assert!(!check!(stat(&file)).perm.contains(io::UserWrite));
1479 match chmod(&tmpdir.join("foo"), io::UserRWX) {
1480 Ok(..) => fail!("wanted a failure"),
1484 check!(chmod(&file, io::UserFile));
1487 iotest!(fn sync_doesnt_kill_anything() {
1488 let tmpdir = tmpdir();
1489 let path = tmpdir.join("in.txt");
1491 let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite));
1492 check!(file.fsync());
1493 check!(file.datasync());
1494 check!(file.write(b"foo"));
1495 check!(file.fsync());
1496 check!(file.datasync());
1500 iotest!(fn truncate_works() {
1501 let tmpdir = tmpdir();
1502 let path = tmpdir.join("in.txt");
1504 let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite));
1505 check!(file.write(b"foo"));
1506 check!(file.fsync());
1508 // Do some simple things with truncation
1509 assert_eq!(check!(file.stat()).size, 3);
1510 check!(file.truncate(10));
1511 assert_eq!(check!(file.stat()).size, 10);
1512 check!(file.write(b"bar"));
1513 check!(file.fsync());
1514 assert_eq!(check!(file.stat()).size, 10);
1515 assert_eq!(check!(File::open(&path).read_to_end()),
1516 (Vec::from_slice(b"foobar\0\0\0\0")));
1518 // Truncate to a smaller length, don't seek, and then write something.
1519 // Ensure that the intermediate zeroes are all filled in (we're seeked
1520 // past the end of the file).
1521 check!(file.truncate(2));
1522 assert_eq!(check!(file.stat()).size, 2);
1523 check!(file.write(b"wut"));
1524 check!(file.fsync());
1525 assert_eq!(check!(file.stat()).size, 9);
1526 assert_eq!(check!(File::open(&path).read_to_end()),
1527 (Vec::from_slice(b"fo\0\0\0\0wut")));
1531 iotest!(fn open_flavors() {
1532 let tmpdir = tmpdir();
1534 match File::open_mode(&tmpdir.join("a"), io::Open, io::Read) {
1535 Ok(..) => fail!(), Err(..) => {}
1538 // Perform each one twice to make sure that it succeeds the second time
1539 // (where the file exists)
1540 check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write));
1541 assert!(tmpdir.join("b").exists());
1542 check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write));
1544 check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite));
1545 assert!(tmpdir.join("c").exists());
1546 check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite));
1548 check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write));
1549 assert!(tmpdir.join("d").exists());
1550 check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write));
1552 check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite));
1553 assert!(tmpdir.join("e").exists());
1554 check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite));
1556 check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write));
1557 assert!(tmpdir.join("f").exists());
1558 check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write));
1560 check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite));
1561 assert!(tmpdir.join("g").exists());
1562 check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite));
1564 check!(File::create(&tmpdir.join("h")).write("foo".as_bytes()));
1565 check!(File::open_mode(&tmpdir.join("h"), io::Open, io::Read));
1567 let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Open,
1569 match f.write("wut".as_bytes()) {
1570 Ok(..) => fail!(), Err(..) => {}
1573 assert!(check!(stat(&tmpdir.join("h"))).size == 3,
1574 "write/stat failed");
1576 let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Append,
1578 check!(f.write("bar".as_bytes()));
1580 assert!(check!(stat(&tmpdir.join("h"))).size == 6,
1581 "append didn't append");
1583 let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Truncate,
1585 check!(f.write("bar".as_bytes()));
1587 assert!(check!(stat(&tmpdir.join("h"))).size == 3,
1588 "truncate didn't truncate");
1593 let tmpdir = tmpdir();
1594 let path = tmpdir.join("a");
1595 check!(File::create(&path));
1597 check!(change_file_times(&path, 1000, 2000));
1598 assert_eq!(check!(path.stat()).accessed, 1000);
1599 assert_eq!(check!(path.stat()).modified, 2000);
1603 fn utime_noexist() {
1604 let tmpdir = tmpdir();
1606 match change_file_times(&tmpdir.join("a"), 100, 200) {
1612 iotest!(fn binary_file() {
1613 use rand::{StdRng, Rng};
1615 let mut bytes = [0, ..1024];
1616 StdRng::new().ok().unwrap().fill_bytes(bytes);
1618 let tmpdir = tmpdir();
1620 check!(File::create(&tmpdir.join("test")).write(bytes));
1621 let actual = check!(File::open(&tmpdir.join("test")).read_to_end());
1622 assert!(actual.as_slice() == bytes);
1625 iotest!(fn unlink_readonly() {
1626 let tmpdir = tmpdir();
1627 let path = tmpdir.join("file");
1628 check!(File::create(&path));
1629 check!(chmod(&path, io::UserRead));
1630 check!(unlink(&path));