| use std::fs::{self, File}; |
| use std::io::{self, Write}; |
| use std::os::windows::fs::symlink_file; |
| #[cfg(miri)] |
| use std::path::{Path, PathBuf}; |
| |
| #[cfg(not(miri))] |
| use tempfile::TempDir; |
| |
| #[cfg(miri)] |
| struct TempDir { |
| path: PathBuf, |
| } |
| |
| #[cfg(miri)] |
| impl TempDir { |
| fn path(&self) -> &Path { |
| self.path.as_path() |
| } |
| } |
| |
| // https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes |
| const ERROR_NOT_A_REPARSE_POINT: i32 = 0x1126; |
| const ERROR_ALREADY_EXISTS: i32 = 0xb7; |
| |
| #[cfg(not(miri))] |
| fn create_tempdir() -> TempDir { |
| tempfile::Builder::new() |
| .prefix("junction-test-") |
| .tempdir_in("target/debug") |
| .unwrap() |
| } |
| |
| #[cfg(miri)] |
| fn create_tempdir() -> TempDir { |
| TempDir { |
| path: PathBuf::from("target/debug/junction-test"), |
| } |
| } |
| |
| #[test] |
| fn create_dir_all_with_junctions() { |
| let tmpdir = create_tempdir(); |
| let target = tmpdir.path().join("target"); |
| |
| let junction = tmpdir.path().join("junction"); |
| let b = junction.join("a/b"); |
| |
| fs::create_dir_all(&target).unwrap(); |
| |
| super::create(&target, &junction).unwrap(); |
| fs::create_dir_all(&b).unwrap(); |
| // the junction itself is not a directory, but `is_dir()` on a Path |
| // follows links |
| assert!(junction.is_dir()); |
| assert!(b.exists()); |
| } |
| |
| #[test] |
| fn create_recursive_rmdir() { |
| let tmpdir = create_tempdir(); |
| let d1 = tmpdir.path().join("d1"); // "d1" |
| let dt = d1.join("t"); // "d1/t" |
| let dtt = dt.join("t"); // "d1/t/t" |
| let d2 = tmpdir.path().join("d2"); // "d2" |
| let canary = d2.join("do_not_delete"); // "d2/do_not_delete" |
| |
| fs::create_dir_all(dtt).unwrap(); |
| fs::create_dir_all(&d2).unwrap(); |
| File::create(&canary).unwrap().write_all(b"foo").unwrap(); |
| |
| super::create(d2, dt.join("d2")).unwrap(); // "d1/t/d2" -> "d2" |
| |
| let _ = symlink_file(&canary, d1.join("canary")); // d1/canary -> d2/do_not_delete |
| fs::remove_dir_all(&d1).unwrap(); |
| |
| assert!(!d1.is_dir()); |
| assert!(canary.exists()); |
| } |
| |
| #[test] |
| fn create_recursive_rmdir_of_symlink() { |
| // test we do not recursively delete a symlink but only dirs. |
| let tmpdir = create_tempdir(); |
| let link = tmpdir.path().join("link"); |
| let dir = tmpdir.path().join("dir"); |
| let canary = dir.join("do_not_delete"); |
| fs::create_dir_all(&dir).unwrap(); |
| File::create(&canary).unwrap().write_all(b"foo").unwrap(); |
| super::create(&dir, &link).unwrap(); |
| fs::remove_dir_all(&link).unwrap(); |
| |
| assert!(!link.is_dir()); |
| assert!(canary.exists()); |
| } |
| |
| #[test] |
| fn create_directory_exist_before() { |
| let tmpdir = create_tempdir(); |
| |
| let target = tmpdir.path().join("target"); |
| let junction = tmpdir.path().join("junction"); |
| |
| fs::create_dir_all(&junction).unwrap(); |
| |
| match super::create(target, &junction) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_ALREADY_EXISTS) => {} |
| other => panic!("directory exists before creating: {:?}", other), |
| } |
| } |
| |
| #[test] |
| fn create_target_no_exist() { |
| let tmpdir = create_tempdir(); |
| |
| let target = tmpdir.path().join("target"); |
| let junction = tmpdir.path().join("junction"); |
| |
| match super::create(target, junction) { |
| Ok(()) => {} |
| other => panic!("junction should point to non exist target path: {:?}", other), |
| } |
| } |
| |
| #[test] |
| fn delete_junctions() { |
| let tmpdir = create_tempdir(); |
| |
| let non_existence_dir = tmpdir.path().join("non_existence_dir"); |
| match super::delete(non_existence_dir) { |
| Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} |
| e => panic!("target path does not exist or is not a directory: {:?}", e), |
| } |
| |
| let dir_not_junction = tmpdir.path().join("dir_not_junction"); |
| fs::create_dir_all(&dir_not_junction).unwrap(); |
| match super::delete(dir_not_junction) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| e => panic!("target path is not a junction point: {:?}", e), |
| } |
| |
| let file = tmpdir.path().join("foo-file"); |
| File::create(&file).unwrap().write_all(b"foo").unwrap(); |
| match super::delete(&file) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| e => panic!("target path is not a junction point: {:?}", e), |
| } |
| } |
| |
| #[test] |
| fn exists_verify() { |
| let tmpdir = create_tempdir(); |
| |
| // Check no such directory or file |
| let no_such_dir = tmpdir.path().join("no_such_dir"); |
| assert!(!super::exists(no_such_dir).unwrap()); |
| |
| // Target exists but not a junction |
| let no_such_file = tmpdir.path().join("file"); |
| File::create(&no_such_file).unwrap().write_all(b"foo").unwrap(); |
| match super::exists(&no_such_file) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| other => panic!("target exists but not a junction: {:?}", other), |
| } |
| |
| let target = tmpdir.path().join("target"); |
| let junction = tmpdir.path().join("junction"); |
| let file = target.join("file"); |
| let junction_file = junction.join("file"); |
| |
| fs::create_dir_all(&target).unwrap(); |
| File::create(file).unwrap().write_all(b"foo").unwrap(); |
| |
| assert!( |
| !junction_file.exists(), |
| "file should not be located until junction created" |
| ); |
| assert!(!super::exists(&junction).unwrap(), "junction not created yet"); |
| |
| super::create(&target, &junction).unwrap(); |
| assert!(super::exists(&junction).unwrap(), "junction should exist now"); |
| assert_eq!(&super::get_target(&junction).unwrap(), &target); |
| assert!(junction_file.exists(), "file should be accessible via the junction"); |
| |
| super::delete(&junction).unwrap(); |
| match super::exists(&junction) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| other => panic!("junction had been deleted: {:?}", other), |
| } |
| assert!( |
| !junction_file.exists(), |
| "file should not be located after junction deleted" |
| ); |
| assert!(junction.exists(), "directory should not be deleted"); |
| } |
| |
| #[test] |
| fn get_target_user_dirs() { |
| use std::env; |
| if cfg!(feature = "unstable_admin") { |
| // These special system directory junction are from |
| // <https://en.wikipedia.org/wiki/NTFS_links#Built-in_uses> |
| assert_eq!( |
| super::get_target(r"C:\Users\Default User").unwrap().to_str(), |
| Some(r"C:\Users\Default"), |
| ); |
| assert_eq!( |
| super::get_target(r"C:\Documents and Settings\").unwrap().to_str(), |
| Some(r"C:\Users"), |
| ); |
| let user_profile = env::var("USERPROFILE").unwrap(); |
| assert_eq!( |
| super::get_target(format!("{user}\\Application Data", user = user_profile)) |
| .unwrap() |
| .to_str(), |
| Some(format!("{user}\\AppData\\Roaming", user = user_profile).as_str()), |
| ); |
| assert_eq!( |
| super::get_target(format!("{user}\\My Documents\\My Pictures", user = user_profile)) |
| .unwrap() |
| .to_str(), |
| Some(format!("{user}\\Pictures", user = user_profile).as_str()), |
| ); |
| } |
| |
| let tmpdir = create_tempdir(); |
| |
| let non_existence_dir = tmpdir.path().join("non_existence_dir"); |
| match super::get_target(non_existence_dir) { |
| Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} |
| other => panic!("target path does not exist or is not a directory: {:?}", other), |
| } |
| |
| let dir_not_junction = tmpdir.path().join("dir_not_junction"); |
| fs::create_dir_all(&dir_not_junction).unwrap(); |
| match super::get_target(dir_not_junction) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| other => panic!("target path is not a junction point: {:?}", other), |
| } |
| |
| let file = tmpdir.path().join("foo-file"); |
| File::create(&file).unwrap().write_all(b"foo").unwrap(); |
| match super::get_target(file) { |
| Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} |
| other => panic!("target path is not a junction point: {:?}", other), |
| } |
| } |