diff options
| author | bors <bors@rust-lang.org> | 2016-02-04 18:48:41 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2016-02-04 18:48:41 +0000 |
| commit | d0ef74026690cffccb543fc274d73a078eba797d (patch) | |
| tree | 53b22502b696e336d3ee7ae6a722fbdbfda77053 /src/libstd/sys | |
| parent | f01b85b1034889888be65d2208640ac926a42d36 (diff) | |
| parent | fb172b676e5ab951e58b98cede795ab1a7557a58 (diff) | |
| download | rust-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.rs | 12 | ||||
| -rw-r--r-- | src/libstd/sys/windows/fs.rs | 201 |
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(|_| ()) } } |
