Skip to content

Commit

Permalink
Add Physical mode to realpath
Browse files Browse the repository at this point in the history
This adds the 'Physical Mode' and 'Logical Mode' switches to realpath, which control when symlinks are resolved.
  • Loading branch information
jaggededgedjustice committed Jun 27, 2021
1 parent 5fce7ec commit 94fc6f1
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 18 deletions.
6 changes: 3 additions & 3 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use std::string::ToString;
use uucore::backup_control::{self, BackupMode};
use uucore::fs::{canonicalize, CanonicalizeMode};
use uucore::fs::{canonicalize, CanonicalizeMode, ResolveMode};
use walkdir::WalkDir;

#[cfg(unix)]
Expand Down Expand Up @@ -1421,8 +1421,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, ResolveMode::Logical)?;
let pathbuf2 = canonicalize(p2, CanonicalizeMode::Normal, ResolveMode::Logical)?;

Ok(pathbuf1 == pathbuf2)
}
Expand Down
10 changes: 7 additions & 3 deletions src/uu/ln/src/ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::os::unix::fs::symlink;
#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};
use std::path::{Path, PathBuf};
use uucore::fs::{canonicalize, CanonicalizeMode};
use uucore::fs::{canonicalize, CanonicalizeMode, ResolveMode};

pub struct Settings {
overwrite: OverwriteMode,
Expand Down Expand Up @@ -375,8 +375,12 @@ 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, ResolveMode::Logical)?;
let mut dst_abs = canonicalize(
dst.parent().unwrap(),
CanonicalizeMode::Normal,
ResolveMode::Logical,
)?;
dst_abs.push(dst.components().last().unwrap());
let suffix_pos = src_abs
.components()
Expand Down
4 changes: 2 additions & 2 deletions src/uu/readlink/src/readlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use clap::{crate_version, App, Arg};
use std::fs;
use std::io::{stdout, Write};
use std::path::{Path, PathBuf};
use uucore::fs::{canonicalize, CanonicalizeMode};
use uucore::fs::{canonicalize, CanonicalizeMode, ResolveMode};

const NAME: &str = "readlink";
const ABOUT: &str = "Print value of a symbolic link or canonical file name.";
Expand Down 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, ResolveMode::Logical) {
Ok(path) => show(&path, no_newline, use_zero),
Err(err) => {
if verbose {
Expand Down
28 changes: 24 additions & 4 deletions src/uu/realpath/src/realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ extern crate uucore;

use clap::{crate_version, App, Arg};
use std::path::{Path, PathBuf};
use uucore::fs::{canonicalize, CanonicalizeMode};
use uucore::fs::{canonicalize, CanonicalizeMode, ResolveMode};

static ABOUT: &str = "print the resolved path";

static OPT_QUIET: &str = "quiet";
static OPT_STRIP: &str = "strip";
static OPT_ZERO: &str = "zero";
static OPT_PHYSICAL: &str = "physical";
static OPT_LOGICAL: &str = "logical";

static ARG_FILES: &str = "files";

Expand All @@ -42,9 +44,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let strip = matches.is_present(OPT_STRIP);
let zero = matches.is_present(OPT_ZERO);
let quiet = matches.is_present(OPT_QUIET);
let logical = matches.is_present(OPT_LOGICAL);
let mut retcode = 0;
for path in &paths {
if let Err(e) = resolve_path(path, strip, zero) {
if let Err(e) = resolve_path(path, strip, zero, logical) {
if !quiet {
show_error!("{}: {}", e, path.display());
}
Expand Down Expand Up @@ -76,6 +79,18 @@ pub fn uu_app() -> App<'static, 'static> {
.long(OPT_ZERO)
.help("Separate output filenames with \\0 rather than newline"),
)
.arg(
Arg::with_name(OPT_LOGICAL)
.short("L")
.long(OPT_LOGICAL)
.help("resolve '..' components before symlinks"),
)
.arg(
Arg::with_name(OPT_PHYSICAL)
.short("P")
.long(OPT_PHYSICAL)
.help("resolve symlinks as encountered (default)"),
)
.arg(
Arg::with_name(ARG_FILES)
.multiple(true)
Expand All @@ -96,13 +111,18 @@ pub fn uu_app() -> App<'static, 'static> {
///
/// 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<()> {
fn resolve_path(p: &Path, strip: bool, zero: bool, logical: bool) -> std::io::Result<()> {
let mode = if strip {
CanonicalizeMode::None
} else {
CanonicalizeMode::Normal
};
let abs = canonicalize(p, mode)?;
let resolve = if logical {
ResolveMode::Logical
} else {
ResolveMode::Physical
};
let abs = canonicalize(p, mode, resolve)?;
let line_ending = if zero { '\0' } else { '\n' };
print!("{}{}", abs.display(), line_ending);
Ok(())
Expand Down
8 changes: 4 additions & 4 deletions src/uu/relpath/src/relpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg};
use std::env;
use std::path::{Path, PathBuf};
use uucore::fs::{canonicalize, CanonicalizeMode};
use uucore::fs::{canonicalize, CanonicalizeMode, ResolveMode};
use uucore::InvalidEncodingHandling;

static ABOUT: &str = "Convert TO destination to the relative path from the FROM dir.
Expand Down 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, ResolveMode::Logical).unwrap();
let absfrom = canonicalize(from, CanonicalizeMode::Normal, ResolveMode::Logical).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, ResolveMode::Logical).unwrap();
if !absto.as_path().starts_with(absbase.as_path())
|| !absfrom.as_path().starts_with(absbase.as_path())
{
Expand Down
30 changes: 28 additions & 2 deletions src/uucore/src/lib/features/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ pub enum CanonicalizeMode {
Missing,
}

/// Controls when symbolic links are resolved
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ResolveMode {
/// Resolve symlinks as encountered when processing the path
Physical,

/// Resolve '..' elements before symlinks
Logical,
}

// copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
// both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
// for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
Expand Down Expand Up @@ -143,7 +153,11 @@ fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
/// * [`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> {
pub fn canonicalize<P: AsRef<Path>>(
original: P,
can_mode: CanonicalizeMode,
res_mode: ResolveMode,
) -> IOResult<PathBuf> {
// Create an absolute path
let original = original.as_ref();
let original = if original.is_absolute() {
Expand All @@ -167,7 +181,11 @@ pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) ->
}
Component::CurDir => (),
Component::ParentDir => {
parts.pop();
if res_mode == ResolveMode::Logical {
parts.pop();
} else {
parts.push(part.as_os_str());
}
}
Component::Normal(_) => {
parts.push(part.as_os_str());
Expand Down Expand Up @@ -210,6 +228,14 @@ pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) ->
}
Err(_) => (),
}
if res_mode == ResolveMode::Physical {
match fs::canonicalize(result) {
Err(e) => return Err(e),
Ok(path) => {
result = path;
}
}
}
}
Ok(result)
}
Expand Down
33 changes: 33 additions & 0 deletions tests/by-util/test_realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,36 @@ fn test_realpath_file_and_links_strip_zero() {
.succeeds()
.stdout_contains("bar\u{0}");
}

#[test]
fn test_realpath_physical_mode() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.mkdir("dir1");
at.mkdir_all("dir2/bar");
at.symlink_dir("dir2/bar", "dir1/foo");

scene
.ucmd()
.arg("dir1/foo/..")
.succeeds()
.stdout_contains("dir2\n");
}

#[test]
fn test_realpath_logical_mode() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.mkdir("dir1");
at.mkdir("dir2");
at.symlink_dir("dir2", "dir1/foo");

scene
.ucmd()
.arg("-L")
.arg("dir1/foo/..")
.succeeds()
.stdout_contains("dir1\n");
}

0 comments on commit 94fc6f1

Please sign in to comment.