From bee3b1237c589f7c2e22d94d0c6d2e99bea0b5b5 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 22 May 2021 10:44:50 -0400 Subject: [PATCH 1/2] uucore::fs: don't canonicalize last component Change the behavior of `uucore::fs::canonicalize()` when `can_mode` is `CanonicalizeMode::None` so that it does not attempt to resolve the final component if it is a symbolic link. This matches the behavior of the function for the non-final components of a path when `can_mode` is `None`. --- src/uucore/src/lib/features/fs.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 040c36e95..afaa07af1 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -54,11 +54,19 @@ pub fn resolve_relative_path(path: &Path) -> Cow { result.into() } +/// Controls how symbolic links should be handled when canonicalizing a path. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CanonicalizeMode { + /// Do not resolve any symbolic links. None, + + /// Resolve all symbolic links. Normal, + + /// Resolve symbolic links, ignoring errors on the final component. Existing, + + /// Resolve symbolic links, ignoring errors on the non-final components. Missing, } @@ -125,6 +133,24 @@ fn resolve>(original: P) -> IOResult { Ok(result) } +/// Return the canonical, absolute form of a path. +/// +/// This function is a generalization of [`std::fs::canonicalize`] that +/// allows controlling how symbolic links are resolved and how to deal +/// with missing components. It returns the canonical, absolute form of +/// a path. The `can_mode` parameter controls how symbolic links are +/// resolved: +/// +/// * [`CanonicalizeMode::Normal`] makes this function behave like +/// [`std::fs::canonicalize`], resolving symbolic links and returning +/// an error if the path does not exist. +/// * [`CanonicalizeMode::Missing`] makes this function ignore non-final +/// components of the path that could not be resolved. +/// * [`CanonicalizeMode::Existing`] makes this function return an error +/// if the final component of the path does not exist. +/// * [`CanonicalizeMode::None`] makes this function not try to resolve +/// any symbolic links. +/// pub fn canonicalize>(original: P, can_mode: CanonicalizeMode) -> IOResult { // Create an absolute path let original = original.as_ref(); @@ -180,6 +206,10 @@ pub fn canonicalize>(original: P, can_mode: CanonicalizeMode) -> result.push(parts.last().unwrap()); + if can_mode == CanonicalizeMode::None { + return Ok(result); + } + match resolve(&result) { Err(e) => { if can_mode == CanonicalizeMode::Existing { From 4b5c3efe85bf3dd735401f40d44eda16dd1c67c2 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 22 May 2021 10:50:00 -0400 Subject: [PATCH 2/2] realpath: use uucore::fs::canonicalize() Use the `uucore::fs::canonicalize()` function to simplify the implementation of `realpath`. --- src/uu/realpath/src/realpath.rs | 78 ++++++++++----------------------- 1 file changed, 24 insertions(+), 54 deletions(-) diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index 37ff70fb2..937cee5bd 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -11,7 +11,6 @@ extern crate uucore; use clap::{App, Arg}; -use std::fs; use std::path::{Path, PathBuf}; use uucore::fs::{canonicalize, CanonicalizeMode}; @@ -75,64 +74,35 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let quiet = matches.is_present(OPT_QUIET); let mut retcode = 0; for path in &paths { - if !resolve_path(path, strip, zero, quiet) { + if let Err(e) = resolve_path(path, strip, zero) { + if !quiet { + show_error!("{}: {}", e, path.display()); + } retcode = 1 }; } retcode } -fn resolve_path(p: &Path, strip: bool, zero: bool, quiet: bool) -> bool { - let abs = canonicalize(p, CanonicalizeMode::Normal).unwrap(); - - if strip { - if zero { - print!("{}\0", p.display()); - } else { - println!("{}", p.display()) - } - return true; - } - - let mut result = PathBuf::new(); - let mut links_left = 256; - - for part in abs.components() { - result.push(part.as_os_str()); - loop { - if links_left == 0 { - if !quiet { - show_error!("Too many symbolic links: {}", p.display()) - }; - return false; - } - match fs::metadata(result.as_path()) { - Err(_) => break, - Ok(ref m) if !m.file_type().is_symlink() => break, - Ok(_) => { - links_left -= 1; - match fs::read_link(result.as_path()) { - Ok(x) => { - result.pop(); - result.push(x.as_path()); - } - _ => { - if !quiet { - show_error!("Invalid path: {}", p.display()) - }; - return false; - } - } - } - } - } - } - - if zero { - print!("{}\0", result.display()); +/// Resolve a path to an absolute form and print it. +/// +/// If `strip` is `true`, then this function does not attempt to resolve +/// symbolic links in the path. If `zero` is `true`, then this function +/// prints the path followed by the null byte (`'\0'`) instead of a +/// newline character (`'\n'`). +/// +/// # Errors +/// +/// This function returns an error if there is a problem resolving +/// symbolic links. +fn resolve_path(p: &Path, strip: bool, zero: bool) -> std::io::Result<()> { + let mode = if strip { + CanonicalizeMode::None } else { - println!("{}", result.display()); - } - - true + CanonicalizeMode::Normal + }; + let abs = canonicalize(p, mode)?; + let line_ending = if zero { '\0' } else { '\n' }; + print!("{}{}", abs.display(), line_ending); + Ok(()) }