diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 1332eb4..1a8c9a8 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -46,7 +46,7 @@ //! - [x] `std::fs::metadata` - `fs.path(path).metadata()` - Given a path, query the file system to get information about a file, directory, etc. //! - [x] `std::fs::read` - `fs.file(path).reader().bytes()` - Read the entire contents of a file into a bytes vector. //! - [x] `std::fs::read_dir` - `fs.dir(path).read()` - Returns an iterator over the entries within a directory. -//! - [ ] `std::fs::read_link` - `link(path).read()` - Reads a symbolic link, returning the file that the link points to. +//! - [x] `std::fs::read_link` - `fs.path(path).read_link()` - Reads a symbolic link, returning the file that the link points to. //! - [x] `std::fs::read_to_string` - `fs.file(path).reader().to_string()` - Read the entire contents of a file into a string. //! - [x] `std::fs::remove_dir` - `fs.dir(path).remove()` - Removes an empty directory. //! - [x] `std::fs::remove_dir_all` - `fs.dir(path).remove_all()` - Removes a directory at this path, after removing all its contents. Use carefully! diff --git a/src/fs/path.rs b/src/fs/path.rs index 7558bde..eb44442 100644 --- a/src/fs/path.rs +++ b/src/fs/path.rs @@ -344,6 +344,13 @@ impl PathReal { self.check_error()?; std::fs::set_permissions(self.as_pathbuf(), perms).map_err(Error::Io) } + + pub fn read_link(&self) -> Result> { + self.check_error()?; + let read_path = std::fs::read_link(self.as_pathbuf()).map_err(Error::Io)?; + let path = read_path.strip_prefix(&self.base).unwrap().to_path_buf(); + Ok(PathReal::new(&self.base, &path)) + } } impl From> for PathBuf { fn from(path: PathReal) -> Self { diff --git a/tests/fs.rs b/tests/fs.rs index 99f7ca5..dfa7bbc 100644 --- a/tests/fs.rs +++ b/tests/fs.rs @@ -32,6 +32,42 @@ mod path { Ok(()) } } + mod read_link { + use super::*; + + #[test] + fn should_read_link() -> TestResult { + let fs = fs::temp().expect("temp fs"); + + let file_path = fs.base().join("foo"); + let file = fs.file(&file_path); + file.write("bar").expect("write"); + + let link_path = fs.base().join("bar"); + let link = fs.path(&link_path); + file.soft_link(&link).expect("soft_link"); + + let read_link = link.read_link().expect("read_link"); + assert_eq!(read_link.as_pathbuf(), file_path); + + Ok(()) + } + + #[test] + fn should_fail_on_path_traversal() -> TestResult { + let fs = fs::temp().expect("temp fs"); + let path = fs.base().join("..").join("foo"); + let_assert!( + Err(fs::Error::PathTraversal { + base: _base, + path: _path + }) = fs.path(&path).read_link() + ); + + Ok(()) + } + } + mod metadata { use super::*;