Skip to content

Commit e6276c6

Browse files
authored
Merge pull request #8528 from id3v1669/main
mv: treat symlinks as files
2 parents 7d2d674 + c53b5db commit e6276c6

2 files changed

Lines changed: 76 additions & 4 deletions

File tree

src/uu/mv/src/mv.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,8 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
375375
});
376376
}
377377

378-
let target_is_dir = target.is_dir();
379-
let source_is_dir = source.is_dir();
378+
let target_is_dir = target.is_dir() && !target.is_symlink();
379+
let source_is_dir = source.is_dir() && !source.is_symlink();
380380

381381
if path_ends_with_terminator(target)
382382
&& (!target_is_dir && !source_is_dir)
@@ -415,7 +415,7 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
415415
} else {
416416
move_files_into_dir(&[source.to_path_buf()], target, opts)
417417
}
418-
} else if target.exists() && source.is_dir() {
418+
} else if target.exists() && source_is_dir {
419419
match opts.overwrite {
420420
OverwriteMode::NoClobber => return Ok(()),
421421
OverwriteMode::Interactive => {
@@ -747,7 +747,7 @@ fn rename(
747747
}
748748

749749
// "to" may no longer exist if it was backed up
750-
if to.exists() && to.is_dir() {
750+
if to.exists() && to.is_dir() && !to.is_symlink() {
751751
// normalize behavior between *nix and windows
752752
if from.is_dir() {
753753
if is_empty_dir(to) {

tests/by-util/test_mv.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,78 @@ fn test_mv_replace_file() {
398398
assert!(at.file_exists(file_b));
399399
}
400400

401+
#[test]
402+
#[cfg(all(unix, not(target_os = "android")))]
403+
fn test_mv_replace_symlink_with_symlink() {
404+
let (at, mut ucmd) = at_and_ucmd!();
405+
406+
at.mkdir("a");
407+
at.mkdir("b");
408+
at.touch("a/empty_file_a");
409+
at.touch("b/empty_file_b");
410+
411+
at.symlink_dir("a", "symlink_a");
412+
at.symlink_dir("b", "symlink_b");
413+
414+
assert_eq!(at.read("symlink_a/empty_file_a"), "");
415+
416+
ucmd.arg("-T")
417+
.arg("symlink_b")
418+
.arg("symlink_a")
419+
.succeeds()
420+
.no_stderr();
421+
422+
assert!(at.file_exists("symlink_a/empty_file_b"));
423+
assert!(!at.file_exists("symlink_a/empty_file_a"));
424+
assert!(!at.symlink_exists("symlink_b"));
425+
}
426+
427+
#[test]
428+
#[cfg(all(unix, not(target_os = "android")))]
429+
fn test_mv_replace_symlink_with_directory() {
430+
let (at, mut ucmd) = at_and_ucmd!();
431+
432+
at.mkdir("a");
433+
at.mkdir("b");
434+
at.touch("a/empty_file_a");
435+
at.touch("b/empty_file_b");
436+
437+
at.symlink_dir("a", "symlink");
438+
439+
ucmd.arg("-T")
440+
.arg("b")
441+
.arg("symlink")
442+
.fails()
443+
.stderr_contains("cannot overwrite non-directory")
444+
.stderr_contains("with directory");
445+
}
446+
447+
#[test]
448+
#[cfg(all(unix, not(target_os = "android")))]
449+
fn test_mv_replace_symlink_with_file() {
450+
let (at, mut ucmd) = at_and_ucmd!();
451+
452+
at.mkdir("a");
453+
at.touch("a/empty_file_a");
454+
at.touch("empty_file_b");
455+
456+
at.symlink_dir("a", "symlink");
457+
458+
assert!(at.file_exists("symlink/empty_file_a"));
459+
460+
ucmd.arg("-T")
461+
.arg("empty_file_b")
462+
.arg("symlink")
463+
.succeeds()
464+
.no_stderr();
465+
466+
assert!(at.file_exists("symlink"));
467+
assert!(!at.is_symlink("symlink"));
468+
assert!(!at.file_exists("empty_file_b"));
469+
assert!(at.dir_exists("a"));
470+
assert!(at.file_exists("a/empty_file_a"));
471+
}
472+
401473
#[test]
402474
fn test_mv_force_replace_file() {
403475
let (at, mut ucmd) = at_and_ucmd!();

0 commit comments

Comments
 (0)