about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-07-14 23:36:23 +0000
committerbors <bors@rust-lang.org>2014-07-14 23:36:23 +0000
commitb733592adb28ed9f6498382ee35b4d0c3d954df6 (patch)
treed73ed8d4df07250492fa7d8e9d9ac111bf9c24e8
parente62479133b9b81ae5c32720fa18bd589a9f425e8 (diff)
parentfe67d269a5f0df45d138051a3f105e05490ccf9e (diff)
downloadrust-b733592adb28ed9f6498382ee35b4d0c3d954df6.tar.gz
rust-b733592adb28ed9f6498382ee35b4d0c3d954df6.zip
auto merge of #15227 : alexcrichton/rust/windows-unlink, r=brson
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.
-rw-r--r--src/librustuv/lib.rs1
-rw-r--r--src/librustuv/uvll.rs4
-rw-r--r--src/libstd/io/fs.rs48
3 files changed, 47 insertions, 6 deletions
diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs
index 4874e123b2b..2c2e134d882 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 863536a4111..a59bc21d792 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 449ad6fa0da..caff7d5e4c5 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));
+    })
 }