1
Fork 0

std: Make unlink() more consistent

Currently when a read-only file has unlink() invoked on it on windows, the call
will fail. On unix, however, the call will succeed. In order to have a more
consistent behavior across platforms, this error is recognized on windows and
the file is changed to read-write before removal is attempted.
This commit is contained in:
Alex Crichton 2014-06-27 14:39:01 -07:00
parent 996263a015
commit fe67d269a5
3 changed files with 47 additions and 6 deletions

View file

@ -420,6 +420,7 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError {
uvll::EADDRNOTAVAIL => libc::WSAEADDRNOTAVAIL,
uvll::ECANCELED => libc::ERROR_OPERATION_ABORTED,
uvll::EADDRINUSE => libc::WSAEADDRINUSE,
uvll::EPERM => libc::ERROR_ACCESS_DENIED,
err => {
uvdebug!("uverr.code {}", err as int);
// FIXME: Need to map remaining uv error types

View file

@ -39,7 +39,7 @@ use libc::uintptr_t;
pub use self::errors::{EACCES, ECONNREFUSED, ECONNRESET, EPIPE, ECONNABORTED,
ECANCELED, EBADF, ENOTCONN, ENOENT, EADDRNOTAVAIL,
EADDRINUSE};
EADDRINUSE, EPERM};
pub static OK: c_int = 0;
pub static EOF: c_int = -4095;
@ -63,6 +63,7 @@ pub mod errors {
pub static EBADF: c_int = -4083;
pub static EADDRNOTAVAIL: c_int = -4090;
pub static EADDRINUSE: c_int = -4091;
pub static EPERM: c_int = -4048;
}
#[cfg(not(windows))]
pub mod errors {
@ -80,6 +81,7 @@ pub mod errors {
pub static EBADF : c_int = -libc::EBADF;
pub static EADDRNOTAVAIL : c_int = -libc::EADDRNOTAVAIL;
pub static EADDRINUSE : c_int = -libc::EADDRINUSE;
pub static EPERM: c_int = -libc::EPERM;
}
pub static PROCESS_SETUID: c_int = 1 << 0;

View file

@ -275,11 +275,41 @@ impl File {
/// user lacks permissions to remove the file, or if some other filesystem-level
/// error occurs.
pub fn unlink(path: &Path) -> IoResult<()> {
let err = LocalIo::maybe_raise(|io| {
io.fs_unlink(&path.to_c_str())
}).map_err(IoError::from_rtio_error);
err.update_err("couldn't unlink path",
|e| format!("{}; path={}", e, path.display()))
return match do_unlink(path) {
Ok(()) => Ok(()),
Err(e) => {
// On unix, a readonly file can be successfully removed. On windows,
// however, it cannot. To keep the two platforms in line with
// respect to their behavior, catch this case on windows, attempt to
// change it to read-write, and then remove the file.
if cfg!(windows) && e.kind == io::PermissionDenied {
let stat = match stat(path) {
Ok(stat) => stat,
Err(..) => return Err(e),
};
if stat.perm.intersects(io::UserWrite) { return Err(e) }
match chmod(path, stat.perm | io::UserWrite) {
Ok(()) => do_unlink(path),
Err(..) => {
// Try to put it back as we found it
let _ = chmod(path, stat.perm);
Err(e)
}
}
} else {
Err(e)
}
}
};
fn do_unlink(path: &Path) -> IoResult<()> {
let err = LocalIo::maybe_raise(|io| {
io.fs_unlink(&path.to_c_str())
}).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,
@ -1591,4 +1621,12 @@ mod test {
let actual = check!(File::open(&tmpdir.join("test")).read_to_end());
assert!(actual.as_slice() == bytes);
})
iotest!(fn unlink_readonly() {
let tmpdir = tmpdir();
let path = tmpdir.join("file");
check!(File::create(&path));
check!(chmod(&path, io::UserRead));
check!(unlink(&path));
})
}