diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index 4874e123b2b8e..2c2e134d882cb 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -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 diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index 863536a411101..a59bc21d792a9 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -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; diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 449ad6fa0dab4..caff7d5e4c593 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -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)); + }) }