Merge pull request #2252 from jfinkels/realpath-simplify

realpath: use uucore::fs::canonicalize() to reduce code duplication
This commit is contained in:
Sylvestre Ledru 2021-05-22 19:10:59 +02:00 committed by GitHub
commit 73fb426b2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 54 deletions

View file

@ -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(())
}

View file

@ -54,11 +54,19 @@ pub fn resolve_relative_path(path: &Path) -> Cow<Path> {
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<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
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<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> IOResult<PathBuf> {
// Create an absolute path
let original = original.as_ref();
@ -180,6 +206,10 @@ pub fn canonicalize<P: AsRef<Path>>(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 {