]> git.lizzy.rs Git - rust.git/commitdiff
Enable more fs tests on Windows
authorPaul Dicker <pitdicker@gmail.com>
Tue, 2 Feb 2016 06:04:55 +0000 (07:04 +0100)
committerPaul Dicker <pitdicker@gmail.com>
Tue, 2 Feb 2016 06:04:55 +0000 (07:04 +0100)
src/libstd/fs.rs
src/libstd/sys/unix/fs.rs
src/libstd/sys/windows/fs.rs

index e40a3d06f77537201336b442ee78d6dc47bc05d6..63980f39c48984dfa7955413431d628b3f71ac5b 100644 (file)
@@ -1253,20 +1253,7 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    _remove_dir_all(path.as_ref())
-}
-
-fn _remove_dir_all(path: &Path) -> io::Result<()> {
-    for child in try!(read_dir(path)) {
-        let child = try!(child).path();
-        let stat = try!(symlink_metadata(&*child));
-        if stat.is_dir() {
-            try!(remove_dir_all(&*child));
-        } else {
-            try!(remove_file(&*child));
-        }
-    }
-    remove_dir(path)
+    fs_imp::remove_dir_all(path.as_ref())
 }
 
 /// Returns an iterator over the entries within a directory.
@@ -1477,19 +1464,25 @@ fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder {
 
 #[cfg(test)]
 mod tests {
-    #![allow(deprecated)] //rand
-
     use prelude::v1::*;
     use io::prelude::*;
 
     use env;
     use fs::{self, File, OpenOptions};
     use io::{ErrorKind, SeekFrom};
-    use path::PathBuf;
-    use path::Path as Path2;
+    use path::{Path, PathBuf};
     use rand::{self, StdRng, Rng};
     use str;
 
+    #[cfg(windows)]
+    use os::windows::fs::{symlink_dir, symlink_file, symlink_junction};
+    #[cfg(unix)]
+    use os::unix::fs::symlink as symlink_dir;
+    #[cfg(unix)]
+    use os::unix::fs::symlink as symlink_file;
+    #[cfg(unix)]
+    use os::unix::fs::symlink as symlink_junction;
+
     macro_rules! check { ($e:expr) => (
         match $e {
             Ok(t) => t,
@@ -1513,7 +1506,7 @@ fn join(&self, path: &str) -> PathBuf {
             p.join(path)
         }
 
-        fn path<'a>(&'a self) -> &'a Path2 {
+        fn path<'a>(&'a self) -> &'a Path {
             let TempDir(ref p) = *self;
             p
         }
@@ -1536,6 +1529,24 @@ pub fn tmpdir() -> TempDir {
         TempDir(ret)
     }
 
+    // Several test fail on windows if the user does not have permission to create symlinks (the
+    // `SeCreateSymbolicLinkPrivilege`). Instead of disabling these test on Windows, use this
+    // function to test whether we have permission, and return otherwise. This way, we still don't
+    // run these tests most of the time, but at least we do if the user has the right permissions.
+    pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
+        let link = tmpdir.join("some_hopefully_unique_link_name");
+
+        match symlink_file(r"nonexisting_target", link) {
+            Ok(_) => true,
+            Err(ref err) =>
+                if err.to_string().contains("A required privilege is not held by the client.") {
+                    false
+                } else {
+                    true
+                }
+        }
+    }
+
     #[test]
     fn file_test_io_smoke_test() {
         let message = "it's alright. have a good time";
@@ -1566,8 +1577,9 @@ fn invalid_path_raises() {
         if cfg!(unix) {
             error!(result, "o such file or directory");
         }
-        // error!(result, "couldn't open path as file");
-        // error!(result, format!("path={}; mode=open; access=read", filename.display()));
+        if cfg!(windows) {
+            error!(result, "The system cannot find the file specified");
+        }
     }
 
     #[test]
@@ -1580,8 +1592,9 @@ fn file_test_iounlinking_invalid_path_should_raise_condition() {
         if cfg!(unix) {
             error!(result, "o such file or directory");
         }
-        // error!(result, "couldn't unlink path");
-        // error!(result, format!("path={}", filename.display()));
+        if cfg!(windows) {
+            error!(result, "The system cannot find the file specified");
+        }
     }
 
     #[test]
@@ -1787,6 +1800,7 @@ fn file_test_directoryinfo_readdir() {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn file_test_walk_dir() {
         let tmpdir = tmpdir();
         let dir = &tmpdir.join("walk_dir");
@@ -1843,19 +1857,13 @@ fn recursive_mkdir_failure() {
         let result = fs::create_dir_all(&file);
 
         assert!(result.is_err());
-        // error!(result, "couldn't recursively mkdir");
-        // error!(result, "couldn't create directory");
-        // error!(result, "mode=0700");
-        // error!(result, format!("path={}", file.display()));
     }
 
     #[test]
     fn recursive_mkdir_slash() {
-        check!(fs::create_dir_all(&Path2::new("/")));
+        check!(fs::create_dir_all(&Path::new("/")));
     }
 
-    // FIXME(#12795) depends on lstat to work on windows
-    #[cfg(not(windows))]
     #[test]
     fn recursive_rmdir() {
         let tmpdir = tmpdir();
@@ -1867,7 +1875,7 @@ fn recursive_rmdir() {
         check!(fs::create_dir_all(&dtt));
         check!(fs::create_dir_all(&d2));
         check!(check!(File::create(&canary)).write(b"foo"));
-        check!(fs::soft_link(&d2, &dt.join("d2")));
+        check!(symlink_junction(&d2, &dt.join("d2")));
         check!(fs::remove_dir_all(&d1));
 
         assert!(!d1.is_dir());
@@ -1876,8 +1884,8 @@ fn recursive_rmdir() {
 
     #[test]
     fn unicode_path_is_dir() {
-        assert!(Path2::new(".").is_dir());
-        assert!(!Path2::new("test/stdtest/fs.rs").is_dir());
+        assert!(Path::new(".").is_dir());
+        assert!(!Path::new("test/stdtest/fs.rs").is_dir());
 
         let tmpdir = tmpdir();
 
@@ -1895,21 +1903,21 @@ fn unicode_path_is_dir() {
 
     #[test]
     fn unicode_path_exists() {
-        assert!(Path2::new(".").exists());
-        assert!(!Path2::new("test/nonexistent-bogus-path").exists());
+        assert!(Path::new(".").exists());
+        assert!(!Path::new("test/nonexistent-bogus-path").exists());
 
         let tmpdir = tmpdir();
         let unicode = tmpdir.path();
         let unicode = unicode.join(&format!("test-각丁ー再见"));
         check!(fs::create_dir(&unicode));
         assert!(unicode.exists());
-        assert!(!Path2::new("test/unicode-bogus-path-각丁ー再见").exists());
+        assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
     }
 
     #[test]
     fn copy_file_does_not_exist() {
-        let from = Path2::new("test/nonexistent-bogus-path");
-        let to = Path2::new("test/other-bogus-path");
+        let from = Path::new("test/nonexistent-bogus-path");
+        let to = Path::new("test/other-bogus-path");
 
         match fs::copy(&from, &to) {
             Ok(..) => panic!(),
@@ -1923,7 +1931,7 @@ fn copy_file_does_not_exist() {
     #[test]
     fn copy_src_does_not_exist() {
         let tmpdir = tmpdir();
-        let from = Path2::new("test/nonexistent-bogus-path");
+        let from = Path::new("test/nonexistent-bogus-path");
         let to = tmpdir.join("out.txt");
         check!(check!(File::create(&to)).write(b"hello"));
         assert!(fs::copy(&from, &to).is_err());
@@ -2014,19 +2022,17 @@ fn copy_file_preserves_streams() {
         assert_eq!(v, b"carrot".to_vec());
     }
 
-    #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
     #[test]
     fn symlinks_work() {
         let tmpdir = tmpdir();
+        if !got_symlink_permission(&tmpdir) { return };
+
         let input = tmpdir.join("in.txt");
         let out = tmpdir.join("out.txt");
 
         check!(check!(File::create(&input)).write("foobar".as_bytes()));
-        check!(fs::soft_link(&input, &out));
-        // if cfg!(not(windows)) {
-        //     assert_eq!(check!(lstat(&out)).kind, FileType::Symlink);
-        //     assert_eq!(check!(out.lstat()).kind, FileType::Symlink);
-        // }
+        check!(symlink_file(&input, &out));
+        assert!(check!(out.symlink_metadata()).file_type().is_symlink());
         assert_eq!(check!(fs::metadata(&out)).len(),
                    check!(fs::metadata(&input)).len());
         let mut v = Vec::new();
@@ -2034,14 +2040,16 @@ fn symlinks_work() {
         assert_eq!(v, b"foobar".to_vec());
     }
 
-    #[cfg(not(windows))] // apparently windows doesn't like symlinks
     #[test]
     fn symlink_noexist() {
+        // Symlinks can point to things that don't exist
         let tmpdir = tmpdir();
-        // symlinks can point to things that don't exist
-        check!(fs::soft_link(&tmpdir.join("foo"), &tmpdir.join("bar")));
-        assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))),
-                   tmpdir.join("foo"));
+        if !got_symlink_permission(&tmpdir) { return };
+
+        // Use a relative path for testing. Symlinks get normalized by Windows, so we may not get
+        // the same path back for absolute paths
+        check!(symlink_file(&"foo", &tmpdir.join("bar")));
+        assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
     }
 
     #[test]
@@ -2312,9 +2320,10 @@ fn canonicalize_works_simple() {
     }
 
     #[test]
-    #[cfg(not(windows))]
     fn realpath_works() {
         let tmpdir = tmpdir();
+        if !got_symlink_permission(&tmpdir) { return };
+
         let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
         let file = tmpdir.join("test");
         let dir = tmpdir.join("test2");
@@ -2323,8 +2332,8 @@ fn realpath_works() {
 
         File::create(&file).unwrap();
         fs::create_dir(&dir).unwrap();
-        fs::soft_link(&file, &link).unwrap();
-        fs::soft_link(&dir, &linkdir).unwrap();
+        symlink_file(&file, &link).unwrap();
+        symlink_dir(&dir, &linkdir).unwrap();
 
         assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
 
@@ -2336,11 +2345,11 @@ fn realpath_works() {
     }
 
     #[test]
-    #[cfg(not(windows))]
     fn realpath_works_tricky() {
         let tmpdir = tmpdir();
-        let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
+        if !got_symlink_permission(&tmpdir) { return };
 
+        let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
         let a = tmpdir.join("a");
         let b = a.join("b");
         let c = b.join("c");
@@ -2351,8 +2360,14 @@ fn realpath_works_tricky() {
         fs::create_dir_all(&b).unwrap();
         fs::create_dir_all(&d).unwrap();
         File::create(&f).unwrap();
-        fs::soft_link("../d/e", &c).unwrap();
-        fs::soft_link("../f", &e).unwrap();
+        if cfg!(not(windows)) {
+            symlink_dir("../d/e", &c).unwrap();
+            symlink_file("../f", &e).unwrap();
+        }
+        if cfg!(windows) {
+            symlink_dir(r"..\d\e", &c).unwrap();
+            symlink_file(r"..\f", &e).unwrap();
+        }
 
         assert_eq!(fs::canonicalize(&c).unwrap(), f);
         assert_eq!(fs::canonicalize(&e).unwrap(), f);
index d2d2ce35d84a0adfe401f80935293cb91929f701..f7989d3571015056f2f7e6a0188024995853bd8c 100644 (file)
@@ -510,6 +510,19 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
     Ok(())
 }
 
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    for child in try!(readdir(path)) {
+        let child = try!(child).path();
+        let stat = try!(lstat(&*child));
+        if stat.file_type().is_dir() {
+            try!(remove_dir_all(&*child));
+        } else {
+            try!(unlink(&*child));
+        }
+    }
+    rmdir(path)
+}
+
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
     let c_path = try!(cstr(p));
     let p = c_path.as_ptr();
index a8b82ef5f297c09e2a99f93b07044eec3e6f511d..17d9bf329df16fb3df1cd0239dc311533a5ba3b2 100644 (file)
@@ -517,6 +517,25 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
     Ok(())
 }
 
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    for child in try!(readdir(path)) {
+        let child = try!(child).path();
+        let stat = try!(lstat(&*child));
+        if stat.data.dwFileAttributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
+            if stat.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+                // remove junctions and directory symlinks with rmdir
+                try!(rmdir(&*child));
+            } else {
+                try!(remove_dir_all(&*child));
+            }
+        } else {
+            // remove files and file symlinks
+            try!(unlink(&*child));
+        }
+    }
+    rmdir(path)
+}
+
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
     let file = try!(File::open_reparse_point(p, false));
     file.readlink()
@@ -635,124 +654,49 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
     Ok(size as u64)
 }
 
-#[test]
-fn directory_junctions_are_directories() {
-    use ffi::OsStr;
-    use env;
-    use rand::{self, Rng};
-    use vec::Vec;
-
-    macro_rules! t {
-        ($e:expr) => (match $e {
-            Ok(e) => e,
-            Err(e) => panic!("{} failed with: {}", stringify!($e), e),
-        })
-    }
+pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
+    symlink_junction_inner(src.as_ref(), dst.as_ref())
+}
 
+// Creating a directory junction on windows involves dealing with reparse
+// points and the DeviceIoControl function, and this code is a skeleton of
+// what can be found here:
+//
+// http://www.flexhex.com/docs/articles/hard-links.phtml
+fn symlink_junction_inner(target: &Path, junction: &Path) -> io::Result<()> {
     let d = DirBuilder::new();
-    let p = env::temp_dir();
-    let mut r = rand::thread_rng();
-    let ret = p.join(&format!("rust-{}", r.next_u32()));
-    let foo = ret.join("foo");
-    let bar = ret.join("bar");
-    t!(d.mkdir(&ret));
-    t!(d.mkdir(&foo));
-    t!(d.mkdir(&bar));
-
-    t!(create_junction(&bar, &foo));
-    let metadata = stat(&bar);
-    t!(delete_junction(&bar));
-
-    t!(rmdir(&foo));
-    t!(rmdir(&bar));
-    t!(rmdir(&ret));
-
-    let metadata = t!(metadata);
-    assert!(metadata.file_type().is_dir());
-
-    // Creating a directory junction on windows involves dealing with reparse
-    // points and the DeviceIoControl function, and this code is a skeleton of
-    // what can be found here:
-    //
-    // http://www.flexhex.com/docs/articles/hard-links.phtml
-    fn create_junction(src: &Path, dst: &Path) -> io::Result<()> {
-        let f = try!(opendir(src, true));
-        let h = f.handle().raw();
+    try!(d.mkdir(&junction));
+    let f = try!(File::open_reparse_point(junction, true));
+    let h = f.handle().raw();
 
-        unsafe {
-            let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-            let mut db = data.as_mut_ptr()
-                            as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
-            let buf = &mut (*db).ReparseTarget as *mut _;
-            let mut i = 0;
-            let v = br"\??\";
-            let v = v.iter().map(|x| *x as u16);
-            for c in v.chain(dst.as_os_str().encode_wide()) {
-                *buf.offset(i) = c;
-                i += 1;
-            }
-            *buf.offset(i) = 0;
+    unsafe {
+        let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+        let mut db = data.as_mut_ptr()
+                        as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
+        let buf = &mut (*db).ReparseTarget as *mut _;
+        let mut i = 0;
+        // FIXME: this conversion is very hacky
+        let v = br"\??\";
+        let v = v.iter().map(|x| *x as u16);
+        for c in v.chain(target.as_os_str().encode_wide()) {
+            *buf.offset(i) = c;
             i += 1;
-            (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
-            (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
-            (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
-            (*db).ReparseDataLength =
-                    (*db).ReparseTargetLength as c::DWORD + 12;
-
-            let mut ret = 0;
-            cvt(c::DeviceIoControl(h as *mut _,
-                                   c::FSCTL_SET_REPARSE_POINT,
-                                   data.as_ptr() as *mut _,
-                                   (*db).ReparseDataLength + 8,
-                                   ptr::null_mut(), 0,
-                                   &mut ret,
-                                   ptr::null_mut())).map(|_| ())
-        }
-    }
-
-    fn opendir(p: &Path, write: bool) -> io::Result<File> {
-        unsafe {
-            let mut token = ptr::null_mut();
-            let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed();
-            try!(cvt(c::OpenProcessToken(c::GetCurrentProcess(),
-                                         c::TOKEN_ADJUST_PRIVILEGES,
-                                         &mut token)));
-            let name: &OsStr = if write {
-                "SeRestorePrivilege".as_ref()
-            } else {
-                "SeBackupPrivilege".as_ref()
-            };
-            let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
-            try!(cvt(c::LookupPrivilegeValueW(ptr::null(),
-                                              name.as_ptr(),
-                                              &mut tp.Privileges[0].Luid)));
-            tp.PrivilegeCount = 1;
-            tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED;
-            let size = mem::size_of::<c::TOKEN_PRIVILEGES>() as c::DWORD;
-            try!(cvt(c::AdjustTokenPrivileges(token, c::FALSE, &mut tp, size,
-                                              ptr::null_mut(), ptr::null_mut())));
-            try!(cvt(c::CloseHandle(token)));
-
-            File::open_reparse_point(p, write)
-        }
-    }
-
-    fn delete_junction(p: &Path) -> io::Result<()> {
-        unsafe {
-            let f = try!(opendir(p, true));
-            let h = f.handle().raw();
-            let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-            let mut db = data.as_mut_ptr()
-                            as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
-            (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
-            let mut bytes = 0;
-            cvt(c::DeviceIoControl(h as *mut _,
-                                   c::FSCTL_DELETE_REPARSE_POINT,
-                                   data.as_ptr() as *mut _,
-                                   (*db).ReparseDataLength + 8,
-                                   ptr::null_mut(), 0,
-                                   &mut bytes,
-                                   ptr::null_mut())).map(|_| ())
         }
+        *buf.offset(i) = 0;
+        i += 1;
+        (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
+        (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
+        (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
+        (*db).ReparseDataLength =
+                (*db).ReparseTargetLength as c::DWORD + 12;
+
+        let mut ret = 0;
+        cvt(c::DeviceIoControl(h as *mut _,
+                               c::FSCTL_SET_REPARSE_POINT,
+                               data.as_ptr() as *mut _,
+                               (*db).ReparseDataLength + 8,
+                               ptr::null_mut(), 0,
+                               &mut ret,
+                               ptr::null_mut())).map(|_| ())
     }
 }