about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-06-27 14:39:01 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-07-14 14:24:50 -0700
commitfe67d269a5f0df45d138051a3f105e05490ccf9e (patch)
tree8d660397d7e5117cc3e5296f0e9904bd2f3cc18d /src/libstd
parent996263a01589c5d2bd2a5ad559abac267296ad71 (diff)
downloadrust-fe67d269a5f0df45d138051a3f105e05490ccf9e.tar.gz
rust-fe67d269a5f0df45d138051a3f105e05490ccf9e.zip
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.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/fs.rs48
1 files changed, 43 insertions, 5 deletions
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));
+    })
 }