mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
ln: manages the 'seen' file list before linking
Should help with tests/mv/childproof.sh
This commit is contained in:
parent
c94773f522
commit
ceecac110c
2 changed files with 57 additions and 1 deletions
|
@ -12,6 +12,7 @@ use uucore::fs::{make_path_relative_to, paths_refer_to_same_file};
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show_error};
|
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show_error};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -295,6 +296,8 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
|
||||||
if !target_dir.is_dir() {
|
if !target_dir.is_dir() {
|
||||||
return Err(LnError::TargetIsDirectory(target_dir.to_owned()).into());
|
return Err(LnError::TargetIsDirectory(target_dir.to_owned()).into());
|
||||||
}
|
}
|
||||||
|
// remember the linked destinations for further usage
|
||||||
|
let mut linked_destinations: HashSet<PathBuf> = HashSet::with_capacity(files.len());
|
||||||
|
|
||||||
let mut all_successful = true;
|
let mut all_successful = true;
|
||||||
for srcpath in files {
|
for srcpath in files {
|
||||||
|
@ -338,10 +341,20 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = link(srcpath, &targetpath, settings) {
|
if linked_destinations.contains(&targetpath) {
|
||||||
|
// If the target file was already created in this linked call, do not overwrite
|
||||||
|
show_error!(
|
||||||
|
"will not overwrite just-created '{}' with '{}'",
|
||||||
|
targetpath.display(),
|
||||||
|
srcpath.display()
|
||||||
|
);
|
||||||
|
all_successful = false;
|
||||||
|
} else if let Err(e) = link(srcpath, &targetpath, settings) {
|
||||||
show_error!("{}", e);
|
show_error!("{}", e);
|
||||||
all_successful = false;
|
all_successful = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linked_destinations.insert(targetpath.clone());
|
||||||
}
|
}
|
||||||
if all_successful {
|
if all_successful {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -719,3 +721,44 @@ fn test_symlink_remove_existing_same_src_and_dest() {
|
||||||
assert!(at.file_exists("a") && !at.symlink_exists("a"));
|
assert!(at.file_exists("a") && !at.symlink_exists("a"));
|
||||||
assert_eq!(at.read("a"), "sample");
|
assert_eq!(at.read("a"), "sample");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ln_seen_file() {
|
||||||
|
let ts = TestScenario::new(util_name!());
|
||||||
|
let at = &ts.fixtures;
|
||||||
|
|
||||||
|
at.mkdir("a");
|
||||||
|
at.mkdir("b");
|
||||||
|
at.mkdir("c");
|
||||||
|
at.write("a/f", "a");
|
||||||
|
at.write("b/f", "b");
|
||||||
|
|
||||||
|
ts.ucmd()
|
||||||
|
.arg("a/f")
|
||||||
|
.arg("b/f")
|
||||||
|
.arg("c")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("will not overwrite just-created 'c/f' with 'b/f'");
|
||||||
|
|
||||||
|
assert!(at.plus("c").join("f").exists());
|
||||||
|
// b/f still exists
|
||||||
|
assert!(at.plus("b").join("f").exists());
|
||||||
|
// a/f no longer exists
|
||||||
|
assert!(at.plus("a").join("f").exists());
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
// Check inode numbers
|
||||||
|
let inode_a_f = at.plus("a").join("f").metadata().unwrap().ino();
|
||||||
|
let inode_b_f = at.plus("b").join("f").metadata().unwrap().ino();
|
||||||
|
let inode_c_f = at.plus("c").join("f").metadata().unwrap().ino();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
inode_a_f, inode_c_f,
|
||||||
|
"Inode numbers of a/f and c/f should be equal"
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
inode_b_f, inode_c_f,
|
||||||
|
"Inode numbers of b/f and c/f should not be equal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue