From bc16746ede246d4884b9a43a4c0322eed3d56da6 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Wed, 28 Jul 2021 18:02:07 -0400 Subject: [PATCH] uucore(fs): add logical arg to canonicalize() Add the `logical` argument to the `uucore::fs::canonicalize()` function. This argument controls whether symbolic links are resolved before or after ".." components are resolved. --- src/uu/cp/src/cp.rs | 4 +-- src/uu/ln/src/ln.rs | 4 +-- src/uu/readlink/src/readlink.rs | 2 +- src/uu/realpath/src/realpath.rs | 2 +- src/uu/relpath/src/relpath.rs | 6 ++-- src/uucore/src/lib/features/fs.rs | 49 +++++++++++++++++++++++-------- 6 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 7c67649c22f..30217e54fb0 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1430,8 +1430,8 @@ pub fn localize_to_target(root: &Path, source: &Path, target: &Path) -> CopyResu pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result { // We have to take symlinks and relative paths into account. - let pathbuf1 = canonicalize(p1, CanonicalizeMode::Normal)?; - let pathbuf2 = canonicalize(p2, CanonicalizeMode::Normal)?; + let pathbuf1 = canonicalize(p1, CanonicalizeMode::Normal, true)?; + let pathbuf2 = canonicalize(p2, CanonicalizeMode::Normal, true)?; Ok(pathbuf1 == pathbuf2) } diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index d354acce934..f83cc66923e 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -361,8 +361,8 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) } fn relative_path<'a>(src: &Path, dst: &Path) -> Result> { - let src_abs = canonicalize(src, CanonicalizeMode::Normal)?; - let mut dst_abs = canonicalize(dst.parent().unwrap(), CanonicalizeMode::Normal)?; + let src_abs = canonicalize(src, CanonicalizeMode::Normal, true)?; + let mut dst_abs = canonicalize(dst.parent().unwrap(), CanonicalizeMode::Normal, true)?; dst_abs.push(dst.components().last().unwrap()); let suffix_pos = src_abs .components() diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index 826fa025418..08b2703181c 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -82,7 +82,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } } else { - match canonicalize(&p, can_mode) { + match canonicalize(&p, can_mode, true) { Ok(path) => show(&path, no_newline, use_zero), Err(err) => { if verbose { diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index fe2ad4ccc47..d7460b3593e 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -102,7 +102,7 @@ fn resolve_path(p: &Path, strip: bool, zero: bool) -> std::io::Result<()> { } else { CanonicalizeMode::Normal }; - let abs = canonicalize(p, mode)?; + let abs = canonicalize(p, mode, true)?; let line_ending = if zero { '\0' } else { '\n' }; print!("{}{}", abs.display(), line_ending); Ok(()) diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs index cb0fba7cc7a..04fb4dca1eb 100644 --- a/src/uu/relpath/src/relpath.rs +++ b/src/uu/relpath/src/relpath.rs @@ -42,12 +42,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Some(p) => Path::new(p).to_path_buf(), None => env::current_dir().unwrap(), }; - let absto = canonicalize(to, CanonicalizeMode::Normal).unwrap(); - let absfrom = canonicalize(from, CanonicalizeMode::Normal).unwrap(); + let absto = canonicalize(to, CanonicalizeMode::Normal, true).unwrap(); + let absfrom = canonicalize(from, CanonicalizeMode::Normal, true).unwrap(); if matches.is_present(options::DIR) { let base = Path::new(&matches.value_of(options::DIR).unwrap()).to_path_buf(); - let absbase = canonicalize(base, CanonicalizeMode::Normal).unwrap(); + let absbase = canonicalize(base, CanonicalizeMode::Normal, true).unwrap(); if !absto.as_path().starts_with(absbase.as_path()) || !absfrom.as_path().starts_with(absbase.as_path()) { diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index e715fae695a..a11d922f1e0 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -102,10 +102,19 @@ pub fn normalize_path(path: &Path) -> PathBuf { ret } -fn resolve>(original: P) -> IOResult { +fn resolve>(original: P, resolve_parents: bool) -> IOResult { const MAX_LINKS_FOLLOWED: u32 = 255; let mut followed = 0; let mut result = original.as_ref().to_path_buf(); + + if resolve_parents { + // TODO Error handling. + while result.components().last().unwrap() == Component::ParentDir { + result.pop(); + result.pop(); + } + } + loop { if followed == MAX_LINKS_FOLLOWED { return Err(Error::new( @@ -136,7 +145,7 @@ fn get_absolute(path: &Path) -> IOResult { } } -fn get_parts(path: &Path) -> (Option<&OsStr>, Vec<&OsStr>) { +fn get_parts(path: &Path, resolve_parents: bool) -> (Option<&OsStr>, Vec<&OsStr>) { let mut parts = vec![]; // Split path by directory separator; add prefix (Windows-only) and root @@ -148,7 +157,11 @@ fn get_parts(path: &Path) -> (Option<&OsStr>, Vec<&OsStr>) { Component::Prefix(_) | Component::RootDir => prefix = Some(part.as_os_str()), Component::CurDir => (), Component::ParentDir => { - parts.pop(); + if resolve_parents { + parts.pop(); + } else { + parts.push(part.as_os_str()); + } } Component::Normal(_) => { parts.push(part.as_os_str()); @@ -171,6 +184,7 @@ fn resolve_all_links( result: &mut PathBuf, parts: Vec<&OsStr>, mode: CanonicalizeMode, + resolve_parents: bool, ) -> IOResult<()> { match mode { // Resolve no links. @@ -181,7 +195,7 @@ fn resolve_all_links( CanonicalizeMode::Missing => { for part in parts { result.push(part); - if let Ok(path) = resolve(&result) { + if let Ok(path) = resolve(&result, resolve_parents) { result.pop(); result.push(path); } @@ -191,7 +205,7 @@ fn resolve_all_links( CanonicalizeMode::Existing => { for part in parts { result.push(part); - let path = resolve(&result)?; + let path = resolve(&result, resolve_parents)?; result.pop(); result.push(path); } @@ -201,13 +215,13 @@ fn resolve_all_links( let n = parts.len(); for part in &parts[..n - 1] { result.push(part); - let path = resolve(&result)?; + let path = resolve(&result, resolve_parents)?; result.pop(); result.push(path); } let p = parts.last().unwrap(); result.push(p); - if let Ok(path) = resolve(&result) { + if let Ok(path) = resolve(&result, resolve_parents) { result.pop(); result.push(path); } @@ -234,12 +248,22 @@ fn resolve_all_links( /// * [`CanonicalizeMode::None`] makes this function not try to resolve /// any symbolic links. /// -pub fn canonicalize>(original: P, can_mode: CanonicalizeMode) -> IOResult { +/// If `logical` is set to `true`, then ".." entries are resolved +/// *before* symbolic links are resolved. If `logical` is set to +/// `false`, then ".." entries are resolved *after* symbolic links are +/// resolved. +pub fn canonicalize>( + original: P, + can_mode: CanonicalizeMode, + logical: bool, +) -> IOResult { // Get the absolute path. For example, convert "a/b" into "/a/b". let absolute_path = get_absolute(original.as_ref())?; - // Convert the absolute path into its components, resolving ".." entries. - let (maybe_prefix, parts) = get_parts(&absolute_path); + // Convert the absolute path into its components, resolving ".." + // entries if requested. + let resolve_parents = logical; + let (maybe_prefix, parts) = get_parts(&absolute_path, resolve_parents); // If there is a prefix, insert it into the `PathBuf` as the first element. let mut result = PathBuf::new(); @@ -252,10 +276,11 @@ pub fn canonicalize>(original: P, can_mode: CanonicalizeMode) -> return Ok(result); } - // Resolve all links in the path. + // Resolve all links in the path, resolving ".." entries if requested. // // This function modifies `result` in-place. - resolve_all_links(&mut result, parts, can_mode)?; + let resolve_parents = !logical; + resolve_all_links(&mut result, parts, can_mode, resolve_parents)?; Ok(result) }