about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-02-04 18:48:41 +0000
committerbors <bors@rust-lang.org>2016-02-04 18:48:41 +0000
commitd0ef74026690cffccb543fc274d73a078eba797d (patch)
tree53b22502b696e336d3ee7ae6a722fbdbfda77053 /src/libstd/sys
parentf01b85b1034889888be65d2208640ac926a42d36 (diff)
parentfb172b676e5ab951e58b98cede795ab1a7557a58 (diff)
downloadrust-d0ef74026690cffccb543fc274d73a078eba797d.tar.gz
rust-d0ef74026690cffccb543fc274d73a078eba797d.zip
Auto merge of #31360 - pitdicker:fs_tests_cleanup, r=alexcrichton
- use `symlink_file` and `symlink_dir` instead of the old `soft_link`
- create a junction instead of a directory symlink for testing recursive_rmdir (as it causes the
  same troubles, but can be created by users without `SeCreateSymbolicLinkPrivilege`)
- `remove_dir_all` was unable to remove directory symlinks and junctions
- only run tests that create symlinks if we have the right permissions.
- rename `Path2` to `Path`
- remove the global `#[allow(deprecated)]` and outdated comments
- After factoring out `create_junction()` from the test `directory_junctions_are_directories` and
  removing needlessly complex code, what I was left with was:
  ```
  #[test]
  #[cfg(windows)]
  fn directory_junctions_are_directories() {
      use sys::fs::create_junction;

      let tmpdir = tmpdir();

      let foo = tmpdir.join("foo");
      let bar = tmpdir.join("bar");

      fs::create_dir(&foo).unwrap();
      check!(create_junction(&foo, &bar));
      assert!(bar.metadata().unwrap().is_dir());
  }
  ```
  It test whether a junction is a directory instead of a reparse point. But it actually test the
  target of the junction (which is a directory if it exists) instead of the junction itself, which
  should always be a symlink. So this test is invalid, and I expect it only exists because the
  author was suprised by it. So I removed it.

Some things that do not yet work right:
- relative symlinks do not accept forward slashes
- the conversion of paths for `create_junction` is hacky
- `remove_dir_all` now messes with the internal data of `FileAttr` to be able to remove symlinks.
  We should add some method like `is_symlink_dir()` to it, so code outside the standard library
  can see the difference between file and directory symlinks too.
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/fs.rs12
-rw-r--r--src/libstd/sys/windows/fs.rs201
2 files changed, 87 insertions, 126 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index efc8bd6cd2e..2527c6774ff 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -554,6 +554,18 @@ 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);
+        if try!(child.file_type()).is_dir() {
+            try!(remove_dir_all(&child.path()));
+        } else {
+            try!(unlink(&child.path()));
+        }
+    }
+    rmdir(path)
+}
+
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
     let c_path = try!(cstr(p));
     let p = c_path.as_ptr();
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index e965a4a1d54..8d921146653 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -35,7 +35,7 @@ pub struct FileAttr {
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub enum FileType {
-    Dir, File, Symlink, ReparsePoint, MountPoint,
+    Dir, File, SymlinkFile, SymlinkDir, ReparsePoint, MountPoint,
 }
 
 pub struct ReadDir {
@@ -450,23 +450,30 @@ impl FilePermissions {
 
 impl FileType {
     fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
-        if attrs & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
-            match reparse_tag {
-                c::IO_REPARSE_TAG_SYMLINK => FileType::Symlink,
-                c::IO_REPARSE_TAG_MOUNT_POINT => FileType::MountPoint,
-                _ => FileType::ReparsePoint,
-            }
-        } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
-            FileType::Dir
-        } else {
-            FileType::File
+        match (attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0,
+               attrs & c::FILE_ATTRIBUTE_REPARSE_POINT != 0,
+               reparse_tag) {
+            (false, false, _) => FileType::File,
+            (true, false, _) => FileType::Dir,
+            (false, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile,
+            (true, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir,
+            (true, true, c::IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint,
+            (_, true, _) => FileType::ReparsePoint,
+            // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
+            // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
+            // to indicate it is something symlink-like, but not something you can follow.
         }
     }
 
     pub fn is_dir(&self) -> bool { *self == FileType::Dir }
     pub fn is_file(&self) -> bool { *self == FileType::File }
     pub fn is_symlink(&self) -> bool {
-        *self == FileType::Symlink || *self == FileType::MountPoint
+        *self == FileType::SymlinkFile ||
+        *self == FileType::SymlinkDir ||
+        *self == FileType::MountPoint
+    }
+    pub fn is_symlink_dir(&self) -> bool {
+        *self == FileType::SymlinkDir || *self == FileType::MountPoint
     }
 }
 
@@ -523,6 +530,21 @@ 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);
+        let child_type = try!(child.file_type());
+        if child_type.is_dir() {
+            try!(remove_dir_all(&child.path()));
+        } else if child_type.is_symlink_dir() {
+            try!(rmdir(&child.path()));
+        } else {
+            try!(unlink(&child.path()));
+        }
+    }
+    rmdir(path)
+}
+
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
     let file = try!(File::open_reparse_point(p, false));
     file.readlink()
@@ -641,124 +663,51 @@ 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),
-        })
-    }
+#[allow(dead_code)]
+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
+#[allow(dead_code)]
+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(|_| ())
     }
 }