use std::ffi::OsString; use std::fs; use realpath_ext::RealpathFlags; #[cfg(feature = "std")] use realpath_ext::realpath; #[cfg(not(feature = "std"))] fn realpath>( path: P, flags: RealpathFlags, ) -> std::io::Result { #[cfg(target_family = "unix")] use std::os::unix::prelude::*; #[cfg(target_os = "wasi")] use std::os::wasi::prelude::*; let mut buf = vec![0; libc::PATH_MAX as usize]; let n = realpath_ext::realpath_raw(path.as_ref().as_os_str().as_bytes(), &mut buf, flags) .map_err(std::io::Error::from_raw_os_error)?; buf.truncate(n); Ok(OsString::from_vec(buf).into()) } #[test] fn test_success() { let tmpdir = std::env::temp_dir(); let exe = std::env::current_exe().unwrap(); let cwd = std::env::current_dir().unwrap(); let mut alt_cwd; if let Some(fname) = cwd.file_name() { alt_cwd = OsString::from("../"); alt_cwd.push(fname); } else { alt_cwd = cwd.clone().into(); } for &path in [ "/", ".", "..", "../..", "..//..//../", "/bin", "///usr/./bin/.", "src", "/etc/passwd", tmpdir.to_str().unwrap(), exe.to_str().unwrap(), cwd.to_str().unwrap(), alt_cwd.to_str().unwrap(), #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "")))] &"./".repeat(libc::PATH_MAX as usize), ] .iter() { assert_eq!( realpath(path, RealpathFlags::empty()).unwrap().as_os_str(), fs::canonicalize(path).unwrap().as_os_str() ); realpath(path, RealpathFlags::IGNORE_SYMLINKS).unwrap(); } } #[test] fn test_leading_slashes() { assert_eq!( realpath("/", RealpathFlags::empty()).unwrap().as_os_str(), "/" ); assert_eq!( realpath("//", RealpathFlags::empty()).unwrap().as_os_str(), "//" ); assert_eq!( realpath("///", RealpathFlags::empty()).unwrap().as_os_str(), "/" ); assert!(!realpath("/", RealpathFlags::empty()) .unwrap() .as_os_str() .to_str() .unwrap() .starts_with("//")); assert!(realpath("//", RealpathFlags::empty()) .unwrap() .as_os_str() .to_str() .unwrap() .starts_with("//")); assert!(!realpath("///", RealpathFlags::empty()) .unwrap() .as_os_str() .to_str() .unwrap() .starts_with("//")); } #[test] fn test_enotdir() { let exe = std::env::current_exe().unwrap().into_os_string(); let mut path = exe.clone(); path.push("/."); assert_eq!( realpath(&path, RealpathFlags::empty()) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath(&path, RealpathFlags::IGNORE_SYMLINKS) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath(&path, RealpathFlags::ALLOW_LAST_MISSING) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); let mut path2 = exe; path2.push("/a/."); assert_eq!( realpath(&path2, RealpathFlags::empty()) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath(&path2, RealpathFlags::IGNORE_SYMLINKS) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); realpath(&path2, RealpathFlags::ALLOW_MISSING).unwrap(); assert_eq!( realpath("/etc/passwd/", RealpathFlags::empty()) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath("/etc/passwd/abc", RealpathFlags::empty()) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath("/etc/passwd/", RealpathFlags::IGNORE_SYMLINKS) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); assert_eq!( realpath("/etc/passwd/abc", RealpathFlags::IGNORE_SYMLINKS) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); realpath("/etc/passwd/", RealpathFlags::ALLOW_MISSING).unwrap(); assert_eq!( realpath("/etc/passwd/", RealpathFlags::ALLOW_LAST_MISSING) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); realpath("/etc/passwd/abc", RealpathFlags::ALLOW_MISSING).unwrap(); assert_eq!( realpath("/etc/passwd/abc", RealpathFlags::ALLOW_LAST_MISSING) .unwrap_err() .raw_os_error(), Some(libc::ENOTDIR) ); } #[test] fn test_enoent() { assert_eq!( realpath("NOEXIST", RealpathFlags::empty()) .unwrap_err() .raw_os_error(), Some(libc::ENOENT) ); realpath("NOEXIST", RealpathFlags::ALLOW_MISSING).unwrap(); realpath("NOEXIST", RealpathFlags::ALLOW_LAST_MISSING).unwrap(); realpath("NOEXIST/abc", RealpathFlags::ALLOW_MISSING).unwrap(); assert_eq!( realpath("NOEXIST/abc", RealpathFlags::ALLOW_LAST_MISSING) .unwrap_err() .raw_os_error(), Some(libc::ENOENT) ); } #[test] fn test_builders() { // Resolving a path with a max length that's too short should fail #[cfg(feature = "std")] assert_eq!( realpath_ext::RealpathBuilder::default() .max_len(10) .realpath("/etc/passwd") .unwrap_err() .raw_os_error(), Some(libc::ENAMETOOLONG), ); // Resolving a path with a buffer that's too short should fail let mut buf = vec![0u8; 10]; assert_eq!( realpath_ext::RealpathRawBuilder::default().realpath_raw(b"/etc/passwd", &mut buf), Err(libc::ENAMETOOLONG), ); // Try some paths that are commonly symlinks for path in ["/bin", "/lib", "/tmp", "/var/run"].iter() { if std::path::Path::new(path).is_symlink() { let mut buf = vec![0u8; 1000]; // This is NOT enough to handle symlinks let mut tmp = vec![0u8; 1]; assert_eq!( realpath_ext::RealpathRawBuilder::new() .temp_buffer(Some(&mut tmp)) .realpath_raw(path.as_bytes(), &mut buf), Err(libc::ENAMETOOLONG), ); } } }