mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
uucore/fs: use the latest resolution that did not fail
When we ignore failures resolving symbolic links we should keep the latest resolution that did not fail, not the original path.
This commit is contained in:
parent
68c9bfa658
commit
ea41cc0ff6
2 changed files with 52 additions and 18 deletions
|
@ -15,6 +15,7 @@ use libc::{
|
|||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Error as IOError;
|
||||
use std::io::Result as IOResult;
|
||||
use std::io::{Error, ErrorKind};
|
||||
#[cfg(any(unix, target_os = "redox"))]
|
||||
|
@ -109,26 +110,42 @@ pub fn normalize_path(path: &Path) -> PathBuf {
|
|||
ret
|
||||
}
|
||||
|
||||
fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
|
||||
fn resolve<P: AsRef<Path>>(original: P) -> Result<PathBuf, (PathBuf, IOError)> {
|
||||
const MAX_LINKS_FOLLOWED: u32 = 255;
|
||||
let mut followed = 0;
|
||||
let mut result = original.as_ref().to_path_buf();
|
||||
|
||||
let mut first_resolution = None;
|
||||
loop {
|
||||
if followed == MAX_LINKS_FOLLOWED {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"maximum links followed",
|
||||
return Err((
|
||||
// When we hit MAX_LINKS_FOLLOWED we should return the first resolution (that's what GNU does - for whatever reason)
|
||||
first_resolution.unwrap(),
|
||||
Error::new(ErrorKind::InvalidInput, "maximum links followed"),
|
||||
));
|
||||
}
|
||||
|
||||
if !fs::symlink_metadata(&result)?.file_type().is_symlink() {
|
||||
break;
|
||||
match fs::symlink_metadata(&result) {
|
||||
Ok(meta) => {
|
||||
if !meta.file_type().is_symlink() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err((result, e)),
|
||||
}
|
||||
|
||||
followed += 1;
|
||||
let path = fs::read_link(&result)?;
|
||||
result.pop();
|
||||
result.push(path);
|
||||
match fs::read_link(&result) {
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
Err(e) => return Err((result, e)),
|
||||
}
|
||||
|
||||
if first_resolution.is_none() {
|
||||
first_resolution = Some(result.clone());
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -214,11 +231,10 @@ pub fn canonicalize<P: AsRef<Path>>(
|
|||
}
|
||||
|
||||
match resolve(&result) {
|
||||
Err(_) if miss_mode == MissingHandling::Missing => continue,
|
||||
Err(e) => return Err(e),
|
||||
Err((path, _)) if miss_mode == MissingHandling::Missing => result = path,
|
||||
Err((_, e)) => return Err(e),
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
result = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,14 +246,12 @@ pub fn canonicalize<P: AsRef<Path>>(
|
|||
}
|
||||
|
||||
match resolve(&result) {
|
||||
Err(e) if miss_mode == MissingHandling::Existing => {
|
||||
Err((_, e)) if miss_mode == MissingHandling::Existing => {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
Ok(path) | Err((path, _)) => {
|
||||
result = path;
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
if res_mode == ResolveMode::Physical {
|
||||
result = normalize_path(&result);
|
||||
|
|
|
@ -139,3 +139,23 @@ fn test_realpath_logical_mode() {
|
|||
.succeeds()
|
||||
.stdout_contains("dir1\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_realpath_dangling() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.symlink_file("nonexistent-file", "link");
|
||||
ucmd.arg("link")
|
||||
.succeeds()
|
||||
.stdout_only(at.plus_as_string("nonexistent-file\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_realpath_loop() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.symlink_file("2", "1");
|
||||
at.symlink_file("3", "2");
|
||||
at.symlink_file("1", "3");
|
||||
ucmd.arg("1")
|
||||
.succeeds()
|
||||
.stdout_only(at.plus_as_string("2\n"));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue