Skip to content

Commit

Permalink
uucore(fs): add logical arg to canonicalize()
Browse files Browse the repository at this point in the history
Add the `logical` argument to the `uucore::fs::canonicalize()`
function. This argument controls whether symbolic links are resolved
before or after ".." components are resolved.
  • Loading branch information
jfinkels committed Jul 28, 2021
1 parent 0d2bc99 commit bc16746
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
// 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)
}
Expand Down
4 changes: 2 additions & 2 deletions src/uu/ln/src/ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Cow<'a, Path>> {
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()
Expand Down
2 changes: 1 addition & 1 deletion src/uu/readlink/src/readlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/uu/realpath/src/realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
6 changes: 3 additions & 3 deletions src/uu/relpath/src/relpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
{
Expand Down
49 changes: 37 additions & 12 deletions src/uucore/src/lib/features/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,19 @@ pub fn normalize_path(path: &Path) -> PathBuf {
ret
}

fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
fn resolve<P: AsRef<Path>>(original: P, resolve_parents: bool) -> IOResult<PathBuf> {
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(
Expand Down Expand Up @@ -136,7 +145,7 @@ fn get_absolute(path: &Path) -> IOResult<PathBuf> {
}
}

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
Expand All @@ -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());
Expand All @@ -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.
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -234,12 +248,22 @@ fn resolve_all_links(
/// * [`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> {
/// 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<P: AsRef<Path>>(
original: P,
can_mode: CanonicalizeMode,
logical: bool,
) -> IOResult<PathBuf> {
// 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();
Expand All @@ -252,10 +276,11 @@ pub fn canonicalize<P: AsRef<Path>>(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)
}

Expand Down

0 comments on commit bc16746

Please sign in to comment.