summaryrefslogtreecommitdiffstats
path: root/vendor/gix-path/src/realpath.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-path/src/realpath.rs
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-path/src/realpath.rs')
-rw-r--r--vendor/gix-path/src/realpath.rs90
1 files changed, 90 insertions, 0 deletions
diff --git a/vendor/gix-path/src/realpath.rs b/vendor/gix-path/src/realpath.rs
new file mode 100644
index 000000000..807cb74cc
--- /dev/null
+++ b/vendor/gix-path/src/realpath.rs
@@ -0,0 +1,90 @@
+/// The error returned by [`realpath()`][super::realpath()].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("The maximum allowed number {} of symlinks in path is exceeded", .max_symlinks)]
+ MaxSymlinksExceeded { max_symlinks: u8 },
+ #[error(transparent)]
+ ReadLink(std::io::Error),
+ #[error(transparent)]
+ CurrentWorkingDir(std::io::Error),
+ #[error("Empty is not a valid path")]
+ EmptyPath,
+ #[error("Ran out of path components while following parent component '..'")]
+ MissingParent,
+}
+
+/// The default amount of symlinks we may follow when resolving a path in [`realpath()`][crate::realpath()].
+pub const MAX_SYMLINKS: u8 = 32;
+
+pub(crate) mod function {
+ use std::path::{
+ Component::{CurDir, Normal, ParentDir, Prefix, RootDir},
+ Path, PathBuf,
+ };
+
+ use super::Error;
+ use crate::realpath::MAX_SYMLINKS;
+
+ /// Check each component of `path` and see if it is a symlink. If so, resolve it.
+ /// Do not fail for non-existing components, but assume these are as is.
+ ///
+ /// If `path` is relative, the current working directory be used to make it absolute.
+ pub fn realpath(path: impl AsRef<Path>) -> Result<PathBuf, Error> {
+ let cwd = path
+ .as_ref()
+ .is_relative()
+ .then(std::env::current_dir)
+ .unwrap_or_else(|| Ok(PathBuf::default()))
+ .map_err(Error::CurrentWorkingDir)?;
+ realpath_opts(path, cwd, MAX_SYMLINKS)
+ }
+
+ /// The same as [`realpath()`], but allow to configure `max_symlinks` to configure how many symbolic links we are going to follow.
+ /// This serves to avoid running into cycles or doing unreasonable amounts of work.
+ pub fn realpath_opts(path: impl AsRef<Path>, cwd: impl AsRef<Path>, max_symlinks: u8) -> Result<PathBuf, Error> {
+ let path = path.as_ref();
+ if path.as_os_str().is_empty() {
+ return Err(Error::EmptyPath);
+ }
+
+ let mut real_path = PathBuf::new();
+ if path.is_relative() {
+ real_path.push(cwd);
+ }
+
+ let mut num_symlinks = 0;
+ let mut path_backing: PathBuf;
+ let mut components = path.components();
+ while let Some(component) = components.next() {
+ match component {
+ part @ RootDir | part @ Prefix(_) => real_path.push(part),
+ CurDir => {}
+ ParentDir => {
+ if !real_path.pop() {
+ return Err(Error::MissingParent);
+ }
+ }
+ Normal(part) => {
+ real_path.push(part);
+ if real_path.is_symlink() {
+ num_symlinks += 1;
+ if num_symlinks > max_symlinks {
+ return Err(Error::MaxSymlinksExceeded { max_symlinks });
+ }
+ let mut link_destination = std::fs::read_link(real_path.as_path()).map_err(Error::ReadLink)?;
+ if link_destination.is_absolute() {
+ // pushing absolute path to real_path resets it to the pushed absolute path
+ } else {
+ assert!(real_path.pop(), "we just pushed a component");
+ }
+ link_destination.extend(components);
+ path_backing = link_destination;
+ components = path_backing.components();
+ }
+ }
+ }
+ }
+ Ok(real_path)
+ }
+}