mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-10 05:53:10 +00:00
Improve error messages for io::fs
This commit is contained in:
parent
2ed4734873
commit
298412a6e8
@ -56,9 +56,9 @@ use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
|||||||
use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
|
use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
|
||||||
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
||||||
use io;
|
use io;
|
||||||
|
use io;
|
||||||
use iter::Iterator;
|
use iter::Iterator;
|
||||||
use kinds::Send;
|
use kinds::Send;
|
||||||
use libc;
|
|
||||||
use option::{Some, None, Option};
|
use option::{Some, None, Option};
|
||||||
use owned::Box;
|
use owned::Box;
|
||||||
use path::{Path, GenericPath};
|
use path::{Path, GenericPath};
|
||||||
@ -138,7 +138,7 @@ impl File {
|
|||||||
Write => rtio::Write,
|
Write => rtio::Write,
|
||||||
ReadWrite => rtio::ReadWrite,
|
ReadWrite => rtio::ReadWrite,
|
||||||
};
|
};
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_open(&path.to_c_str(), mode, access).map(|fd| {
|
io.fs_open(&path.to_c_str(), mode, access).map(|fd| {
|
||||||
File {
|
File {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
@ -146,7 +146,11 @@ impl File {
|
|||||||
last_nread: -1
|
last_nread: -1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't open file", |e| {
|
||||||
|
format!("{}; path={}; mode={}; access={}", e, path.display(),
|
||||||
|
mode_string(mode), access_string(access))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to open a file in read-only mode. This function is equivalent to
|
/// Attempts to open a file in read-only mode. This function is equivalent to
|
||||||
@ -185,6 +189,7 @@ impl File {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn create(path: &Path) -> IoResult<File> {
|
pub fn create(path: &Path) -> IoResult<File> {
|
||||||
File::open_mode(path, Truncate, Write)
|
File::open_mode(path, Truncate, Write)
|
||||||
|
.update_desc("couldn't create file")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the original path which was used to open this file.
|
/// Returns the original path which was used to open this file.
|
||||||
@ -196,7 +201,9 @@ impl File {
|
|||||||
/// device. This will flush any internal buffers necessary to perform this
|
/// device. This will flush any internal buffers necessary to perform this
|
||||||
/// operation.
|
/// operation.
|
||||||
pub fn fsync(&mut self) -> IoResult<()> {
|
pub fn fsync(&mut self) -> IoResult<()> {
|
||||||
self.fd.fsync().map_err(IoError::from_rtio_error)
|
let err = self.fd.fsync().map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't fsync file",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is similar to `fsync`, except that it may not synchronize
|
/// This function is similar to `fsync`, except that it may not synchronize
|
||||||
@ -204,7 +211,9 @@ impl File {
|
|||||||
/// must synchronize content, but don't need the metadata on disk. The goal
|
/// must synchronize content, but don't need the metadata on disk. The goal
|
||||||
/// of this method is to reduce disk operations.
|
/// of this method is to reduce disk operations.
|
||||||
pub fn datasync(&mut self) -> IoResult<()> {
|
pub fn datasync(&mut self) -> IoResult<()> {
|
||||||
self.fd.datasync().map_err(IoError::from_rtio_error)
|
let err = self.fd.datasync().map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't datasync file",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either truncates or extends the underlying file, updating the size of
|
/// Either truncates or extends the underlying file, updating the size of
|
||||||
@ -216,7 +225,10 @@ impl File {
|
|||||||
/// will be extended to `size` and have all of the intermediate data filled
|
/// will be extended to `size` and have all of the intermediate data filled
|
||||||
/// in with 0s.
|
/// in with 0s.
|
||||||
pub fn truncate(&mut self, size: i64) -> IoResult<()> {
|
pub fn truncate(&mut self, size: i64) -> IoResult<()> {
|
||||||
self.fd.truncate(size).map_err(IoError::from_rtio_error)
|
let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't truncate file", |e| {
|
||||||
|
format!("{}; path={}; size={}", e, self.path.display(), size)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests whether this stream has reached EOF.
|
/// Tests whether this stream has reached EOF.
|
||||||
@ -229,10 +241,12 @@ impl File {
|
|||||||
|
|
||||||
/// Queries information about the underlying file.
|
/// Queries information about the underlying file.
|
||||||
pub fn stat(&mut self) -> IoResult<FileStat> {
|
pub fn stat(&mut self) -> IoResult<FileStat> {
|
||||||
match self.fd.fstat() {
|
let err = match self.fd.fstat() {
|
||||||
Ok(s) => Ok(from_rtio(s)),
|
Ok(s) => Ok(from_rtio(s)),
|
||||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||||
}
|
};
|
||||||
|
err.update_err("couldn't fstat file",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,9 +272,11 @@ impl File {
|
|||||||
/// user lacks permissions to remove the file, or if some other filesystem-level
|
/// user lacks permissions to remove the file, or if some other filesystem-level
|
||||||
/// error occurs.
|
/// error occurs.
|
||||||
pub fn unlink(path: &Path) -> IoResult<()> {
|
pub fn unlink(path: &Path) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_unlink(&path.to_c_str())
|
io.fs_unlink(&path.to_c_str())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't unlink path",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a path, query the file system to get information about a file,
|
/// Given a path, query the file system to get information about a file,
|
||||||
@ -285,10 +301,12 @@ pub fn unlink(path: &Path) -> IoResult<()> {
|
|||||||
/// to perform a `stat` call on the given path or if there is no entry in the
|
/// to perform a `stat` call on the given path or if there is no entry in the
|
||||||
/// filesystem at the provided path.
|
/// filesystem at the provided path.
|
||||||
pub fn stat(path: &Path) -> IoResult<FileStat> {
|
pub fn stat(path: &Path) -> IoResult<FileStat> {
|
||||||
match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
|
let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
|
||||||
Ok(s) => Ok(from_rtio(s)),
|
Ok(s) => Ok(from_rtio(s)),
|
||||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||||
}
|
};
|
||||||
|
err.update_err("couldn't stat path",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the same operation as the `stat` function, except that this
|
/// Perform the same operation as the `stat` function, except that this
|
||||||
@ -300,10 +318,12 @@ pub fn stat(path: &Path) -> IoResult<FileStat> {
|
|||||||
///
|
///
|
||||||
/// See `stat`
|
/// See `stat`
|
||||||
pub fn lstat(path: &Path) -> IoResult<FileStat> {
|
pub fn lstat(path: &Path) -> IoResult<FileStat> {
|
||||||
match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
|
let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
|
||||||
Ok(s) => Ok(from_rtio(s)),
|
Ok(s) => Ok(from_rtio(s)),
|
||||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||||
}
|
};
|
||||||
|
err.update_err("couldn't lstat path",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_rtio(s: rtio::FileStat) -> FileStat {
|
fn from_rtio(s: rtio::FileStat) -> FileStat {
|
||||||
@ -359,9 +379,12 @@ fn from_rtio(s: rtio::FileStat) -> FileStat {
|
|||||||
/// permissions to view the contents, or if some other intermittent I/O error
|
/// permissions to view the contents, or if some other intermittent I/O error
|
||||||
/// occurs.
|
/// occurs.
|
||||||
pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_rename(&from.to_c_str(), &to.to_c_str())
|
io.fs_rename(&from.to_c_str(), &to.to_c_str())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't rename path", |e| {
|
||||||
|
format!("{}; from={}; to={}", e, from.display(), to.display())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies the contents of one file to another. This function will also
|
/// Copies the contents of one file to another. This function will also
|
||||||
@ -393,12 +416,17 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
|||||||
/// ensured to not exist, there is nothing preventing the destination from
|
/// ensured to not exist, there is nothing preventing the destination from
|
||||||
/// being created and then destroyed by this operation.
|
/// being created and then destroyed by this operation.
|
||||||
pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
||||||
|
fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
|
||||||
|
result.update_err("couldn't copy path",
|
||||||
|
|e| format!("{}; from={}; to={}", e, from.display(), to.display()))
|
||||||
|
}
|
||||||
|
|
||||||
if !from.is_file() {
|
if !from.is_file() {
|
||||||
return Err(IoError {
|
return update_err(Err(IoError {
|
||||||
kind: io::MismatchedFileTypeForOperation,
|
kind: io::MismatchedFileTypeForOperation,
|
||||||
desc: "the source path is not an existing file",
|
desc: "the source path is not an existing file",
|
||||||
detail: None,
|
detail: None
|
||||||
})
|
}), from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut reader = try!(File::open(from));
|
let mut reader = try!(File::open(from));
|
||||||
@ -409,12 +437,12 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
|||||||
let amt = match reader.read(buf) {
|
let amt = match reader.read(buf) {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(ref e) if e.kind == io::EndOfFile => { break }
|
Err(ref e) if e.kind == io::EndOfFile => { break }
|
||||||
Err(e) => return Err(e)
|
Err(e) => return update_err(Err(e), from, to)
|
||||||
};
|
};
|
||||||
try!(writer.write(buf.slice_to(amt)));
|
try!(writer.write(buf.slice_to(amt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
chmod(to, try!(from.stat()).perm)
|
chmod(to, try!(update_err(from.stat(), from, to)).perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the permission mode bits found on a file or a directory. This
|
/// Changes the permission mode bits found on a file or a directory. This
|
||||||
@ -439,33 +467,45 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
|||||||
/// Some possible error situations are not having the permission to
|
/// Some possible error situations are not having the permission to
|
||||||
/// change the attributes of a file or the file not existing.
|
/// change the attributes of a file or the file not existing.
|
||||||
pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
|
pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
|
io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't chmod path", |e| {
|
||||||
|
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the user and group owners of a file at the specified path.
|
/// Change the user and group owners of a file at the specified path.
|
||||||
pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
|
pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_chown(&path.to_c_str(), uid, gid)
|
io.fs_chown(&path.to_c_str(), uid, gid)
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't chown path", |e| {
|
||||||
|
format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new hard link on the filesystem. The `dst` path will be a
|
/// Creates a new hard link on the filesystem. The `dst` path will be a
|
||||||
/// link pointing to the `src` path. Note that systems often require these
|
/// link pointing to the `src` path. Note that systems often require these
|
||||||
/// two paths to both be located on the same filesystem.
|
/// two paths to both be located on the same filesystem.
|
||||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_link(&src.to_c_str(), &dst.to_c_str())
|
io.fs_link(&src.to_c_str(), &dst.to_c_str())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't link path", |e| {
|
||||||
|
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new symbolic link on the filesystem. The `dst` path will be a
|
/// Creates a new symbolic link on the filesystem. The `dst` path will be a
|
||||||
/// symlink pointing to the `src` path.
|
/// symlink pointing to the `src` path.
|
||||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
|
io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't symlink path", |e| {
|
||||||
|
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a symlink, returning the file that the symlink points to.
|
/// Reads a symlink, returning the file that the symlink points to.
|
||||||
@ -475,9 +515,11 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
|||||||
/// This function will return an error on failure. Failure conditions include
|
/// This function will return an error on failure. Failure conditions include
|
||||||
/// reading a file that does not exist or reading a file which is not a symlink.
|
/// reading a file that does not exist or reading a file which is not a symlink.
|
||||||
pub fn readlink(path: &Path) -> IoResult<Path> {
|
pub fn readlink(path: &Path) -> IoResult<Path> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
|
Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't resolve symlink for path",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new, empty directory at the provided path
|
/// Create a new, empty directory at the provided path
|
||||||
@ -498,9 +540,12 @@ pub fn readlink(path: &Path) -> IoResult<Path> {
|
|||||||
/// This call will return an error if the user lacks permissions to make a new
|
/// This call will return an error if the user lacks permissions to make a new
|
||||||
/// directory at the provided path, or if the directory already exists.
|
/// directory at the provided path, or if the directory already exists.
|
||||||
pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
|
io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't create directory", |e| {
|
||||||
|
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove an existing, empty directory
|
/// Remove an existing, empty directory
|
||||||
@ -520,9 +565,11 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
|||||||
/// This call will return an error if the user lacks permissions to remove the
|
/// This call will return an error if the user lacks permissions to remove the
|
||||||
/// directory at the provided path, or if the directory isn't empty.
|
/// directory at the provided path, or if the directory isn't empty.
|
||||||
pub fn rmdir(path: &Path) -> IoResult<()> {
|
pub fn rmdir(path: &Path) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_rmdir(&path.to_c_str())
|
io.fs_rmdir(&path.to_c_str())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't remove directory",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a vector containing all entries within a provided directory
|
/// Retrieve a vector containing all entries within a provided directory
|
||||||
@ -557,11 +604,13 @@ pub fn rmdir(path: &Path) -> IoResult<()> {
|
|||||||
/// permissions to view the contents or if the `path` points at a non-directory
|
/// permissions to view the contents or if the `path` points at a non-directory
|
||||||
/// file
|
/// file
|
||||||
pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).move_iter().map(|a| {
|
Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).move_iter().map(|a| {
|
||||||
Path::new(a)
|
Path::new(a)
|
||||||
}).collect())
|
}).collect())
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't read directory",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator which will recursively walk the directory structure
|
/// Returns an iterator which will recursively walk the directory structure
|
||||||
@ -569,7 +618,11 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
|||||||
/// perform iteration in some top-down order. The contents of unreadable
|
/// perform iteration in some top-down order. The contents of unreadable
|
||||||
/// subdirectories are ignored.
|
/// subdirectories are ignored.
|
||||||
pub fn walk_dir(path: &Path) -> IoResult<Directories> {
|
pub fn walk_dir(path: &Path) -> IoResult<Directories> {
|
||||||
Ok(Directories { stack: try!(readdir(path)) })
|
Ok(Directories {
|
||||||
|
stack: try!(readdir(path).update_err("couldn't walk directory",
|
||||||
|
|e| format!("{}; path={}",
|
||||||
|
e, path.display())))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator which walks over a directory
|
/// An iterator which walks over a directory
|
||||||
@ -582,7 +635,12 @@ impl Iterator<Path> for Directories {
|
|||||||
match self.stack.pop() {
|
match self.stack.pop() {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
match readdir(&path) {
|
let result = readdir(&path)
|
||||||
|
.update_err("couldn't advance Directories iterator",
|
||||||
|
|e| format!("{}; path={}",
|
||||||
|
e, path.display()));
|
||||||
|
|
||||||
|
match result {
|
||||||
Ok(dirs) => { self.stack.push_all_move(dirs); }
|
Ok(dirs) => { self.stack.push_all_move(dirs); }
|
||||||
Err(..) => {}
|
Err(..) => {}
|
||||||
}
|
}
|
||||||
@ -614,7 +672,11 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
|
|||||||
for c in comps {
|
for c in comps {
|
||||||
curpath.push(c);
|
curpath.push(c);
|
||||||
|
|
||||||
match mkdir(&curpath, mode) {
|
let result = mkdir(&curpath, mode)
|
||||||
|
.update_err("couldn't recursively mkdir",
|
||||||
|
|e| format!("{}; path={}", e, path.display()));
|
||||||
|
|
||||||
|
match result {
|
||||||
Err(mkdir_err) => {
|
Err(mkdir_err) => {
|
||||||
// already exists ?
|
// already exists ?
|
||||||
if try!(stat(&curpath)).kind != io::TypeDirectory {
|
if try!(stat(&curpath)).kind != io::TypeDirectory {
|
||||||
@ -639,8 +701,20 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
|||||||
let mut rm_stack = Vec::new();
|
let mut rm_stack = Vec::new();
|
||||||
rm_stack.push(path.clone());
|
rm_stack.push(path.clone());
|
||||||
|
|
||||||
|
fn rmdir_failed(err: &IoError, path: &Path) -> String {
|
||||||
|
format!("rmdir_recursive failed; path={}; cause={}",
|
||||||
|
path.display(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_err<T>(err: IoResult<T>, path: &Path) -> IoResult<T> {
|
||||||
|
err.update_err("couldn't recursively rmdir",
|
||||||
|
|e| rmdir_failed(e, path))
|
||||||
|
}
|
||||||
|
|
||||||
while !rm_stack.is_empty() {
|
while !rm_stack.is_empty() {
|
||||||
let children = try!(readdir(rm_stack.last().unwrap()));
|
let children = try!(readdir(rm_stack.last().unwrap())
|
||||||
|
.update_detail(|e| rmdir_failed(e, path)));
|
||||||
|
|
||||||
let mut has_child_dir = false;
|
let mut has_child_dir = false;
|
||||||
|
|
||||||
// delete all regular files in the way and push subdirs
|
// delete all regular files in the way and push subdirs
|
||||||
@ -648,17 +722,17 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
|||||||
for child in children.move_iter() {
|
for child in children.move_iter() {
|
||||||
// FIXME(#12795) we should use lstat in all cases
|
// FIXME(#12795) we should use lstat in all cases
|
||||||
let child_type = match cfg!(windows) {
|
let child_type = match cfg!(windows) {
|
||||||
true => try!(stat(&child)).kind,
|
true => try!(update_err(stat(&child), path)),
|
||||||
false => try!(lstat(&child)).kind
|
false => try!(update_err(lstat(&child), path))
|
||||||
};
|
};
|
||||||
|
|
||||||
if child_type == io::TypeDirectory {
|
if child_type.kind == io::TypeDirectory {
|
||||||
rm_stack.push(child);
|
rm_stack.push(child);
|
||||||
has_child_dir = true;
|
has_child_dir = true;
|
||||||
} else {
|
} else {
|
||||||
// we can carry on safely if the file is already gone
|
// we can carry on safely if the file is already gone
|
||||||
// (eg: deleted by someone else since readdir)
|
// (eg: deleted by someone else since readdir)
|
||||||
match unlink(&child) {
|
match update_err(unlink(&child), path) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref e) if e.kind == io::FileNotFound => (),
|
Err(ref e) if e.kind == io::FileNotFound => (),
|
||||||
Err(e) => return Err(e)
|
Err(e) => return Err(e)
|
||||||
@ -668,7 +742,8 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
|||||||
|
|
||||||
// if no subdir was found, let's pop and delete
|
// if no subdir was found, let's pop and delete
|
||||||
if !has_child_dir {
|
if !has_child_dir {
|
||||||
match rmdir(&rm_stack.pop().unwrap()) {
|
let result = update_err(rmdir(&rm_stack.pop().unwrap()), path);
|
||||||
|
match result {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref e) if e.kind == io::FileNotFound => (),
|
Err(ref e) if e.kind == io::FileNotFound => (),
|
||||||
Err(e) => return Err(e)
|
Err(e) => return Err(e)
|
||||||
@ -685,18 +760,28 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
|||||||
/// be in milliseconds.
|
/// be in milliseconds.
|
||||||
// FIXME(#10301) these arguments should not be u64
|
// FIXME(#10301) these arguments should not be u64
|
||||||
pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||||
LocalIo::maybe_raise(|io| {
|
let err = LocalIo::maybe_raise(|io| {
|
||||||
io.fs_utime(&path.to_c_str(), atime, mtime)
|
io.fs_utime(&path.to_c_str(), atime, mtime)
|
||||||
}).map_err(IoError::from_rtio_error)
|
}).map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't change_file_times",
|
||||||
|
|e| format!("{}; path={}", e, path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reader for File {
|
impl Reader for File {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||||
match self.fd.read(buf) {
|
fn update_err<T>(result: IoResult<T>, file: &File) -> IoResult<T> {
|
||||||
|
result.update_err("couldn't read file",
|
||||||
|
|e| format!("{}; path={}",
|
||||||
|
e, file.path.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: IoResult<int> = update_err(self.fd.read(buf), self);
|
||||||
|
|
||||||
|
match result {
|
||||||
Ok(read) => {
|
Ok(read) => {
|
||||||
self.last_nread = read;
|
self.last_nread = read;
|
||||||
match read {
|
match read {
|
||||||
0 => Err(io::standard_error(io::EndOfFile)),
|
0 => update_err(Err(standard_error(io::EndOfFile)), self),
|
||||||
_ => Ok(read as uint)
|
_ => Ok(read as uint)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -707,13 +792,17 @@ impl Reader for File {
|
|||||||
|
|
||||||
impl Writer for File {
|
impl Writer for File {
|
||||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||||
self.fd.write(buf).map_err(IoError::from_rtio_error)
|
let err = self.fd.write(buf).map_err(IoError::from_rtio_error)
|
||||||
|
err.update_err("couldn't write to file",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Seek for File {
|
impl Seek for File {
|
||||||
fn tell(&self) -> IoResult<u64> {
|
fn tell(&self) -> IoResult<u64> {
|
||||||
self.fd.tell().map_err(IoError::from_rtio_error)
|
let err = self.fd.tell().map_err(IoError::from_rtio_error);
|
||||||
|
err.update_err("couldn't retrieve file cursor (`tell`)",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||||
@ -722,14 +811,16 @@ impl Seek for File {
|
|||||||
SeekCur => rtio::SeekCur,
|
SeekCur => rtio::SeekCur,
|
||||||
SeekEnd => rtio::SeekEnd,
|
SeekEnd => rtio::SeekEnd,
|
||||||
};
|
};
|
||||||
match self.fd.seek(pos, style) {
|
let err = match self.fd.seek(pos, style) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// successful seek resets EOF indicator
|
// successful seek resets EOF indicator
|
||||||
self.last_nread = -1;
|
self.last_nread = -1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||||
}
|
};
|
||||||
|
err.update_err("couldn't seek in file",
|
||||||
|
|e| format!("{}; path={}", e, self.path.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,6 +870,22 @@ impl path::Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mode_string(mode: FileMode) -> &'static str {
|
||||||
|
match mode {
|
||||||
|
super::Open => "open",
|
||||||
|
super::Append => "append",
|
||||||
|
super::Truncate => "truncate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access_string(access: FileAccess) -> &'static str {
|
||||||
|
match access {
|
||||||
|
super::Read => "read",
|
||||||
|
super::Write => "write",
|
||||||
|
super::ReadWrite => "readwrite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
mod test {
|
mod test {
|
||||||
@ -801,6 +908,14 @@ mod test {
|
|||||||
}
|
}
|
||||||
) )
|
) )
|
||||||
|
|
||||||
|
macro_rules! error( ($e:expr, $s:expr) => (
|
||||||
|
match $e {
|
||||||
|
Ok(val) => fail!("Should have been an error, was {:?}", val),
|
||||||
|
Err(ref err) => assert!(err.to_str().as_slice().contains($s.as_slice()),
|
||||||
|
format!("`{}` did not contain `{}`", err, $s))
|
||||||
|
}
|
||||||
|
) )
|
||||||
|
|
||||||
struct TempDir(Path);
|
struct TempDir(Path);
|
||||||
|
|
||||||
impl TempDir {
|
impl TempDir {
|
||||||
@ -856,13 +971,21 @@ mod test {
|
|||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let filename = &tmpdir.join("file_that_does_not_exist.txt");
|
let filename = &tmpdir.join("file_that_does_not_exist.txt");
|
||||||
let result = File::open_mode(filename, Open, Read);
|
let result = File::open_mode(filename, Open, Read);
|
||||||
assert!(result.is_err());
|
|
||||||
|
error!(result, "couldn't open file");
|
||||||
|
error!(result, "no such file or directory");
|
||||||
|
error!(result, format!("path={}; mode=open; access=read", filename.display()));
|
||||||
})
|
})
|
||||||
|
|
||||||
iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() {
|
iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
|
let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
|
||||||
assert!(unlink(filename).is_err());
|
|
||||||
|
let result = unlink(filename);
|
||||||
|
|
||||||
|
error!(result, "couldn't unlink path");
|
||||||
|
error!(result, "no such file or directory");
|
||||||
|
error!(result, format!("path={}", filename.display()));
|
||||||
})
|
})
|
||||||
|
|
||||||
iotest!(fn file_test_io_non_positional_read() {
|
iotest!(fn file_test_io_non_positional_read() {
|
||||||
@ -1091,6 +1214,22 @@ mod test {
|
|||||||
assert!(dir.is_dir())
|
assert!(dir.is_dir())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
iotest!(fn recursive_mkdir_failure() {
|
||||||
|
let tmpdir = tmpdir();
|
||||||
|
let dir = tmpdir.join("d1");
|
||||||
|
let file = dir.join("f1");
|
||||||
|
|
||||||
|
check!(mkdir_recursive(&dir, io::UserRWX));
|
||||||
|
check!(File::create(&file));
|
||||||
|
|
||||||
|
let result = mkdir_recursive(&file, io::UserRWX);
|
||||||
|
|
||||||
|
error!(result, "couldn't recursively mkdir");
|
||||||
|
error!(result, "couldn't create directory");
|
||||||
|
error!(result, "mode=FilePermission { bits: 448 }");
|
||||||
|
error!(result, format!("path={}", file.display()));
|
||||||
|
})
|
||||||
|
|
||||||
iotest!(fn recursive_mkdir_slash() {
|
iotest!(fn recursive_mkdir_slash() {
|
||||||
check!(mkdir_recursive(&Path::new("/"), io::UserRWX));
|
check!(mkdir_recursive(&Path::new("/"), io::UserRWX));
|
||||||
})
|
})
|
||||||
@ -1147,6 +1286,12 @@ mod test {
|
|||||||
iotest!(fn copy_file_does_not_exist() {
|
iotest!(fn copy_file_does_not_exist() {
|
||||||
let from = Path::new("test/nonexistent-bogus-path");
|
let from = Path::new("test/nonexistent-bogus-path");
|
||||||
let to = Path::new("test/other-bogus-path");
|
let to = Path::new("test/other-bogus-path");
|
||||||
|
|
||||||
|
error!(copy(&from, &to),
|
||||||
|
format!("couldn't copy path (the source path is not an \
|
||||||
|
existing file; from={}; to={})",
|
||||||
|
from.display(), to.display()));
|
||||||
|
|
||||||
match copy(&from, &to) {
|
match copy(&from, &to) {
|
||||||
Ok(..) => fail!(),
|
Ok(..) => fail!(),
|
||||||
Err(..) => {
|
Err(..) => {
|
||||||
|
@ -232,7 +232,7 @@ use owned::Box;
|
|||||||
use result::{Ok, Err, Result};
|
use result::{Ok, Err, Result};
|
||||||
use rt::rtio;
|
use rt::rtio;
|
||||||
use slice::{Vector, MutableVector, ImmutableVector};
|
use slice::{Vector, MutableVector, ImmutableVector};
|
||||||
use str::{StrSlice, StrAllocating};
|
use str::{Str, StrSlice, StrAllocating};
|
||||||
use str;
|
use str;
|
||||||
use string::String;
|
use string::String;
|
||||||
use uint;
|
use uint;
|
||||||
@ -309,6 +309,7 @@ impl IoError {
|
|||||||
/// struct is filled with an allocated string describing the error
|
/// struct is filled with an allocated string describing the error
|
||||||
/// in more detail, retrieved from the operating system.
|
/// in more detail, retrieved from the operating system.
|
||||||
pub fn from_errno(errno: uint, detail: bool) -> IoError {
|
pub fn from_errno(errno: uint, detail: bool) -> IoError {
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
||||||
match errno {
|
match errno {
|
||||||
@ -388,8 +389,8 @@ impl IoError {
|
|||||||
IoError {
|
IoError {
|
||||||
kind: kind,
|
kind: kind,
|
||||||
desc: desc,
|
desc: desc,
|
||||||
detail: if detail {
|
detail: if detail && kind == OtherIoError {
|
||||||
Some(os::error_string(errno))
|
Some(os::error_string(errno).as_slice().chars().map(|c| c.to_lowercase()).collect())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -420,10 +421,13 @@ impl IoError {
|
|||||||
|
|
||||||
impl fmt::Show for IoError {
|
impl fmt::Show for IoError {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
try!(write!(fmt, "{}", self.desc));
|
match *self {
|
||||||
match self.detail {
|
IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } =>
|
||||||
Some(ref s) => write!(fmt, " ({})", *s),
|
write!(fmt, "{}", detail),
|
||||||
None => Ok(())
|
IoError { detail: None, desc, .. } =>
|
||||||
|
write!(fmt, "{}", desc),
|
||||||
|
IoError { detail: Some(ref detail), desc, .. } =>
|
||||||
|
write!(fmt, "{} ({})", desc, detail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,6 +488,37 @@ pub enum IoErrorKind {
|
|||||||
NoProgress,
|
NoProgress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait that lets you add a `detail` to an IoError easily
|
||||||
|
trait UpdateIoError<T> {
|
||||||
|
/// Returns an IoError with updated description and detail
|
||||||
|
fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> Self;
|
||||||
|
|
||||||
|
/// Returns an IoError with updated detail
|
||||||
|
fn update_detail(self, detail: |&IoError| -> String) -> Self;
|
||||||
|
|
||||||
|
/// Returns an IoError with update description
|
||||||
|
fn update_desc(self, desc: &'static str) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UpdateIoError<T> for IoResult<T> {
|
||||||
|
fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> IoResult<T> {
|
||||||
|
self.map_err(|mut e| {
|
||||||
|
let detail = detail(&e);
|
||||||
|
e.desc = desc;
|
||||||
|
e.detail = Some(detail);
|
||||||
|
e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_detail(self, detail: |&IoError| -> String) -> IoResult<T> {
|
||||||
|
self.map_err(|mut e| { e.detail = Some(detail(&e)); e })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_desc(self, desc: &'static str) -> IoResult<T> {
|
||||||
|
self.map_err(|mut e| { e.desc = desc; e })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static NO_PROGRESS_LIMIT: uint = 1000;
|
static NO_PROGRESS_LIMIT: uint = 1000;
|
||||||
|
|
||||||
/// A trait for objects which are byte-oriented streams. Readers are defined by
|
/// A trait for objects which are byte-oriented streams. Readers are defined by
|
||||||
@ -1577,7 +1612,7 @@ pub fn standard_error(kind: IoErrorKind) -> IoError {
|
|||||||
ConnectionAborted => "connection aborted",
|
ConnectionAborted => "connection aborted",
|
||||||
NotConnected => "not connected",
|
NotConnected => "not connected",
|
||||||
BrokenPipe => "broken pipe",
|
BrokenPipe => "broken pipe",
|
||||||
PathAlreadyExists => "file exists",
|
PathAlreadyExists => "file already exists",
|
||||||
PathDoesntExist => "no such file",
|
PathDoesntExist => "no such file",
|
||||||
MismatchedFileTypeForOperation => "mismatched file type",
|
MismatchedFileTypeForOperation => "mismatched file type",
|
||||||
ResourceUnavailable => "resource unavailable",
|
ResourceUnavailable => "resource unavailable",
|
||||||
|
Loading…
Reference in New Issue
Block a user