mirror of
https://github.com/uutils/coreutils
synced 2024-12-12 14:22:41 +00:00
ln
: allow final destination directory when using -nf
(#5975)
Closes: #5974
This commit is contained in:
parent
39bfa40acf
commit
564dd47297
2 changed files with 97 additions and 36 deletions
|
@ -301,45 +301,45 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
|
|||
|
||||
let mut all_successful = true;
|
||||
for srcpath in files {
|
||||
let targetpath =
|
||||
if settings.no_dereference && matches!(settings.overwrite, OverwriteMode::Force) {
|
||||
// In that case, we don't want to do link resolution
|
||||
// We need to clean the target
|
||||
if target_dir.is_symlink() {
|
||||
if target_dir.is_file() {
|
||||
if let Err(e) = fs::remove_file(target_dir) {
|
||||
show_error!("Could not update {}: {}", target_dir.quote(), e);
|
||||
};
|
||||
}
|
||||
if target_dir.is_dir() {
|
||||
// Not sure why but on Windows, the symlink can be
|
||||
// considered as a dir
|
||||
// See test_ln::test_symlink_no_deref_dir
|
||||
if let Err(e) = fs::remove_dir(target_dir) {
|
||||
show_error!("Could not update {}: {}", target_dir.quote(), e);
|
||||
};
|
||||
let targetpath = if settings.no_dereference
|
||||
&& matches!(settings.overwrite, OverwriteMode::Force)
|
||||
&& target_dir.is_symlink()
|
||||
{
|
||||
// In that case, we don't want to do link resolution
|
||||
// We need to clean the target
|
||||
if target_dir.is_file() {
|
||||
if let Err(e) = fs::remove_file(target_dir) {
|
||||
show_error!("Could not update {}: {}", target_dir.quote(), e);
|
||||
};
|
||||
}
|
||||
if target_dir.is_dir() {
|
||||
// Not sure why but on Windows, the symlink can be
|
||||
// considered as a dir
|
||||
// See test_ln::test_symlink_no_deref_dir
|
||||
if let Err(e) = fs::remove_dir(target_dir) {
|
||||
show_error!("Could not update {}: {}", target_dir.quote(), e);
|
||||
};
|
||||
}
|
||||
target_dir.to_path_buf()
|
||||
} else {
|
||||
match srcpath.as_os_str().to_str() {
|
||||
Some(name) => {
|
||||
match Path::new(name).file_name() {
|
||||
Some(basename) => target_dir.join(basename),
|
||||
// This can be None only for "." or "..". Trying
|
||||
// to create a link with such name will fail with
|
||||
// EEXIST, which agrees with the behavior of GNU
|
||||
// coreutils.
|
||||
None => target_dir.join(name),
|
||||
}
|
||||
}
|
||||
target_dir.to_path_buf()
|
||||
} else {
|
||||
match srcpath.as_os_str().to_str() {
|
||||
Some(name) => {
|
||||
match Path::new(name).file_name() {
|
||||
Some(basename) => target_dir.join(basename),
|
||||
// This can be None only for "." or "..". Trying
|
||||
// to create a link with such name will fail with
|
||||
// EEXIST, which agrees with the behavior of GNU
|
||||
// coreutils.
|
||||
None => target_dir.join(name),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
show_error!("cannot stat {}: No such file or directory", srcpath.quote());
|
||||
all_successful = false;
|
||||
continue;
|
||||
}
|
||||
None => {
|
||||
show_error!("cannot stat {}: No such file or directory", srcpath.quote());
|
||||
all_successful = false;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if linked_destinations.contains(&targetpath) {
|
||||
// If the target file was already created in this ln call, do not overwrite
|
||||
|
|
|
@ -549,6 +549,67 @@ fn test_symlink_no_deref_dir() {
|
|||
assert_eq!(at.resolve_link(link), dir1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symlink_no_deref_file_in_destination_dir() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file1 = "foo";
|
||||
let file2 = "bar";
|
||||
|
||||
let dest = "baz";
|
||||
|
||||
let link1 = "baz/foo";
|
||||
let link2 = "baz/bar";
|
||||
|
||||
at.touch(file1);
|
||||
at.touch(file2);
|
||||
at.mkdir(dest);
|
||||
|
||||
assert!(at.file_exists(file1));
|
||||
assert!(at.file_exists(file2));
|
||||
assert!(at.dir_exists(dest));
|
||||
|
||||
// -n and -f should work alone
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-sn", file1, dest])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
assert!(at.is_symlink(link1));
|
||||
assert_eq!(at.resolve_link(link1), file1);
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-sf", file1, dest])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
assert!(at.is_symlink(link1));
|
||||
assert_eq!(at.resolve_link(link1), file1);
|
||||
|
||||
// -n alone should fail if destination exists already (it should now)
|
||||
scene.ucmd().args(&["-sn", file1, dest]).fails();
|
||||
|
||||
// -nf should also work
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-snf", file1, dest])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
assert!(at.is_symlink(link1));
|
||||
assert_eq!(at.resolve_link(link1), file1);
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-snf", file1, file2, dest])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
assert!(at.is_symlink(link1));
|
||||
assert_eq!(at.resolve_link(link1), file1);
|
||||
assert!(at.is_symlink(link2));
|
||||
assert_eq!(at.resolve_link(link2), file2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symlink_no_deref_file() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
|
Loading…
Reference in a new issue