mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Enable more fs tests on Windows
This commit is contained in:
parent
b16fbe79ac
commit
cdb1df749e
129
src/libstd/fs.rs
129
src/libstd/fs.rs
@ -1253,20 +1253,7 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
_remove_dir_all(path.as_ref())
|
fs_imp::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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the entries within a directory.
|
/// Returns an iterator over the entries within a directory.
|
||||||
@ -1477,19 +1464,25 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(deprecated)] //rand
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
use io::prelude::*;
|
use io::prelude::*;
|
||||||
|
|
||||||
use env;
|
use env;
|
||||||
use fs::{self, File, OpenOptions};
|
use fs::{self, File, OpenOptions};
|
||||||
use io::{ErrorKind, SeekFrom};
|
use io::{ErrorKind, SeekFrom};
|
||||||
use path::PathBuf;
|
use path::{Path, PathBuf};
|
||||||
use path::Path as Path2;
|
|
||||||
use rand::{self, StdRng, Rng};
|
use rand::{self, StdRng, Rng};
|
||||||
use str;
|
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) => (
|
macro_rules! check { ($e:expr) => (
|
||||||
match $e {
|
match $e {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
@ -1513,7 +1506,7 @@ mod tests {
|
|||||||
p.join(path)
|
p.join(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path<'a>(&'a self) -> &'a Path2 {
|
fn path<'a>(&'a self) -> &'a Path {
|
||||||
let TempDir(ref p) = *self;
|
let TempDir(ref p) = *self;
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
@ -1536,6 +1529,24 @@ mod tests {
|
|||||||
TempDir(ret)
|
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]
|
#[test]
|
||||||
fn file_test_io_smoke_test() {
|
fn file_test_io_smoke_test() {
|
||||||
let message = "it's alright. have a good time";
|
let message = "it's alright. have a good time";
|
||||||
@ -1566,8 +1577,9 @@ mod tests {
|
|||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
error!(result, "o such file or directory");
|
error!(result, "o such file or directory");
|
||||||
}
|
}
|
||||||
// error!(result, "couldn't open path as file");
|
if cfg!(windows) {
|
||||||
// error!(result, format!("path={}; mode=open; access=read", filename.display()));
|
error!(result, "The system cannot find the file specified");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1580,8 +1592,9 @@ mod tests {
|
|||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
error!(result, "o such file or directory");
|
error!(result, "o such file or directory");
|
||||||
}
|
}
|
||||||
// error!(result, "couldn't unlink path");
|
if cfg!(windows) {
|
||||||
// error!(result, format!("path={}", filename.display()));
|
error!(result, "The system cannot find the file specified");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1787,6 +1800,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(deprecated)]
|
||||||
fn file_test_walk_dir() {
|
fn file_test_walk_dir() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let dir = &tmpdir.join("walk_dir");
|
let dir = &tmpdir.join("walk_dir");
|
||||||
@ -1843,19 +1857,13 @@ mod tests {
|
|||||||
let result = fs::create_dir_all(&file);
|
let result = fs::create_dir_all(&file);
|
||||||
|
|
||||||
assert!(result.is_err());
|
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]
|
#[test]
|
||||||
fn recursive_mkdir_slash() {
|
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]
|
#[test]
|
||||||
fn recursive_rmdir() {
|
fn recursive_rmdir() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
@ -1867,7 +1875,7 @@ mod tests {
|
|||||||
check!(fs::create_dir_all(&dtt));
|
check!(fs::create_dir_all(&dtt));
|
||||||
check!(fs::create_dir_all(&d2));
|
check!(fs::create_dir_all(&d2));
|
||||||
check!(check!(File::create(&canary)).write(b"foo"));
|
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));
|
check!(fs::remove_dir_all(&d1));
|
||||||
|
|
||||||
assert!(!d1.is_dir());
|
assert!(!d1.is_dir());
|
||||||
@ -1876,8 +1884,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unicode_path_is_dir() {
|
fn unicode_path_is_dir() {
|
||||||
assert!(Path2::new(".").is_dir());
|
assert!(Path::new(".").is_dir());
|
||||||
assert!(!Path2::new("test/stdtest/fs.rs").is_dir());
|
assert!(!Path::new("test/stdtest/fs.rs").is_dir());
|
||||||
|
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
|
|
||||||
@ -1895,21 +1903,21 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unicode_path_exists() {
|
fn unicode_path_exists() {
|
||||||
assert!(Path2::new(".").exists());
|
assert!(Path::new(".").exists());
|
||||||
assert!(!Path2::new("test/nonexistent-bogus-path").exists());
|
assert!(!Path::new("test/nonexistent-bogus-path").exists());
|
||||||
|
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let unicode = tmpdir.path();
|
let unicode = tmpdir.path();
|
||||||
let unicode = unicode.join(&format!("test-각丁ー再见"));
|
let unicode = unicode.join(&format!("test-각丁ー再见"));
|
||||||
check!(fs::create_dir(&unicode));
|
check!(fs::create_dir(&unicode));
|
||||||
assert!(unicode.exists());
|
assert!(unicode.exists());
|
||||||
assert!(!Path2::new("test/unicode-bogus-path-각丁ー再见").exists());
|
assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn copy_file_does_not_exist() {
|
fn copy_file_does_not_exist() {
|
||||||
let from = Path2::new("test/nonexistent-bogus-path");
|
let from = Path::new("test/nonexistent-bogus-path");
|
||||||
let to = Path2::new("test/other-bogus-path");
|
let to = Path::new("test/other-bogus-path");
|
||||||
|
|
||||||
match fs::copy(&from, &to) {
|
match fs::copy(&from, &to) {
|
||||||
Ok(..) => panic!(),
|
Ok(..) => panic!(),
|
||||||
@ -1923,7 +1931,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn copy_src_does_not_exist() {
|
fn copy_src_does_not_exist() {
|
||||||
let tmpdir = tmpdir();
|
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");
|
let to = tmpdir.join("out.txt");
|
||||||
check!(check!(File::create(&to)).write(b"hello"));
|
check!(check!(File::create(&to)).write(b"hello"));
|
||||||
assert!(fs::copy(&from, &to).is_err());
|
assert!(fs::copy(&from, &to).is_err());
|
||||||
@ -2014,19 +2022,17 @@ mod tests {
|
|||||||
assert_eq!(v, b"carrot".to_vec());
|
assert_eq!(v, b"carrot".to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))] // FIXME(#10264) operation not permitted?
|
|
||||||
#[test]
|
#[test]
|
||||||
fn symlinks_work() {
|
fn symlinks_work() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
|
if !got_symlink_permission(&tmpdir) { return };
|
||||||
|
|
||||||
let input = tmpdir.join("in.txt");
|
let input = tmpdir.join("in.txt");
|
||||||
let out = tmpdir.join("out.txt");
|
let out = tmpdir.join("out.txt");
|
||||||
|
|
||||||
check!(check!(File::create(&input)).write("foobar".as_bytes()));
|
check!(check!(File::create(&input)).write("foobar".as_bytes()));
|
||||||
check!(fs::soft_link(&input, &out));
|
check!(symlink_file(&input, &out));
|
||||||
// if cfg!(not(windows)) {
|
assert!(check!(out.symlink_metadata()).file_type().is_symlink());
|
||||||
// assert_eq!(check!(lstat(&out)).kind, FileType::Symlink);
|
|
||||||
// assert_eq!(check!(out.lstat()).kind, FileType::Symlink);
|
|
||||||
// }
|
|
||||||
assert_eq!(check!(fs::metadata(&out)).len(),
|
assert_eq!(check!(fs::metadata(&out)).len(),
|
||||||
check!(fs::metadata(&input)).len());
|
check!(fs::metadata(&input)).len());
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
@ -2034,14 +2040,16 @@ mod tests {
|
|||||||
assert_eq!(v, b"foobar".to_vec());
|
assert_eq!(v, b"foobar".to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))] // apparently windows doesn't like symlinks
|
|
||||||
#[test]
|
#[test]
|
||||||
fn symlink_noexist() {
|
fn symlink_noexist() {
|
||||||
|
// Symlinks can point to things that don't exist
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
// symlinks can point to things that don't exist
|
if !got_symlink_permission(&tmpdir) { return };
|
||||||
check!(fs::soft_link(&tmpdir.join("foo"), &tmpdir.join("bar")));
|
|
||||||
assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))),
|
// Use a relative path for testing. Symlinks get normalized by Windows, so we may not get
|
||||||
tmpdir.join("foo"));
|
// 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]
|
#[test]
|
||||||
@ -2312,9 +2320,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn realpath_works() {
|
fn realpath_works() {
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
|
if !got_symlink_permission(&tmpdir) { return };
|
||||||
|
|
||||||
let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
|
let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
|
||||||
let file = tmpdir.join("test");
|
let file = tmpdir.join("test");
|
||||||
let dir = tmpdir.join("test2");
|
let dir = tmpdir.join("test2");
|
||||||
@ -2323,8 +2332,8 @@ mod tests {
|
|||||||
|
|
||||||
File::create(&file).unwrap();
|
File::create(&file).unwrap();
|
||||||
fs::create_dir(&dir).unwrap();
|
fs::create_dir(&dir).unwrap();
|
||||||
fs::soft_link(&file, &link).unwrap();
|
symlink_file(&file, &link).unwrap();
|
||||||
fs::soft_link(&dir, &linkdir).unwrap();
|
symlink_dir(&dir, &linkdir).unwrap();
|
||||||
|
|
||||||
assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
|
assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
|
||||||
|
|
||||||
@ -2336,11 +2345,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn realpath_works_tricky() {
|
fn realpath_works_tricky() {
|
||||||
let tmpdir = tmpdir();
|
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 a = tmpdir.join("a");
|
||||||
let b = a.join("b");
|
let b = a.join("b");
|
||||||
let c = b.join("c");
|
let c = b.join("c");
|
||||||
@ -2351,8 +2360,14 @@ mod tests {
|
|||||||
fs::create_dir_all(&b).unwrap();
|
fs::create_dir_all(&b).unwrap();
|
||||||
fs::create_dir_all(&d).unwrap();
|
fs::create_dir_all(&d).unwrap();
|
||||||
File::create(&f).unwrap();
|
File::create(&f).unwrap();
|
||||||
fs::soft_link("../d/e", &c).unwrap();
|
if cfg!(not(windows)) {
|
||||||
fs::soft_link("../f", &e).unwrap();
|
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(&c).unwrap(), f);
|
||||||
assert_eq!(fs::canonicalize(&e).unwrap(), f);
|
assert_eq!(fs::canonicalize(&e).unwrap(), f);
|
||||||
|
@ -510,6 +510,19 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
|
|||||||
Ok(())
|
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> {
|
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
||||||
let c_path = try!(cstr(p));
|
let c_path = try!(cstr(p));
|
||||||
let p = c_path.as_ptr();
|
let p = c_path.as_ptr();
|
||||||
|
@ -517,6 +517,25 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
|
|||||||
Ok(())
|
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> {
|
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
||||||
let file = try!(File::open_reparse_point(p, false));
|
let file = try!(File::open_reparse_point(p, false));
|
||||||
file.readlink()
|
file.readlink()
|
||||||
@ -635,124 +654,49 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
|||||||
Ok(size as u64)
|
Ok(size as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||||
fn directory_junctions_are_directories() {
|
symlink_junction_inner(src.as_ref(), dst.as_ref())
|
||||||
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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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 d = DirBuilder::new();
|
||||||
let p = env::temp_dir();
|
try!(d.mkdir(&junction));
|
||||||
let mut r = rand::thread_rng();
|
let f = try!(File::open_reparse_point(junction, true));
|
||||||
let ret = p.join(&format!("rust-{}", r.next_u32()));
|
let h = f.handle().raw();
|
||||||
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));
|
unsafe {
|
||||||
let metadata = stat(&bar);
|
let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||||
t!(delete_junction(&bar));
|
let mut db = data.as_mut_ptr()
|
||||||
|
as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
|
||||||
t!(rmdir(&foo));
|
let buf = &mut (*db).ReparseTarget as *mut _;
|
||||||
t!(rmdir(&bar));
|
let mut i = 0;
|
||||||
t!(rmdir(&ret));
|
// FIXME: this conversion is very hacky
|
||||||
|
let v = br"\??\";
|
||||||
let metadata = t!(metadata);
|
let v = v.iter().map(|x| *x as u16);
|
||||||
assert!(metadata.file_type().is_dir());
|
for c in v.chain(target.as_os_str().encode_wide()) {
|
||||||
|
*buf.offset(i) = c;
|
||||||
// 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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
i += 1;
|
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(|_| ())
|
|
||||||
}
|
}
|
||||||
}
|
*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;
|
||||||
|
|
||||||
fn opendir(p: &Path, write: bool) -> io::Result<File> {
|
let mut ret = 0;
|
||||||
unsafe {
|
cvt(c::DeviceIoControl(h as *mut _,
|
||||||
let mut token = ptr::null_mut();
|
c::FSCTL_SET_REPARSE_POINT,
|
||||||
let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed();
|
data.as_ptr() as *mut _,
|
||||||
try!(cvt(c::OpenProcessToken(c::GetCurrentProcess(),
|
(*db).ReparseDataLength + 8,
|
||||||
c::TOKEN_ADJUST_PRIVILEGES,
|
ptr::null_mut(), 0,
|
||||||
&mut token)));
|
&mut ret,
|
||||||
let name: &OsStr = if write {
|
ptr::null_mut())).map(|_| ())
|
||||||
"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(|_| ())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user