ln: accept non-utf8 source names in target-dir modes (#11403)

* ln: accept non-utf8 source names in target-dir modes

* ln: remove unused ln-error-cannot-stat locale strings
This commit is contained in:
Can Bölük
2026-06-06 19:40:42 +02:00
committed by GitHub
parent fab0cbd327
commit 48d030dfb2
4 changed files with 48 additions and 12 deletions
-1
View File
@@ -29,7 +29,6 @@ ln-error-missing-destination = missing destination file operand after {$operand}
ln-error-extra-operand = extra operand {$operand}
Try '{$program} --help' for more information.
ln-error-could-not-update = Could not update {$target}: {$error}
ln-error-cannot-stat = cannot stat {$path}: No such file or directory
ln-error-will-not-overwrite = will not overwrite just-created {$target} with {$source}
ln-prompt-replace = replace {$file}?
ln-cannot-backup = cannot backup {$file}
-1
View File
@@ -30,7 +30,6 @@ ln-error-missing-destination = opérande de fichier de destination manquant apr
ln-error-extra-operand = opérande supplémentaire {$operand}
Essayez « {$program} --help » pour plus d'informations.
ln-error-could-not-update = Impossible de mettre à jour {$target} : {$error}
ln-error-cannot-stat = impossible d'analyser {$path} : Aucun fichier ou répertoire de ce nom
ln-error-will-not-overwrite = ne remplacera pas le fichier {$target} qui vient d'être créé par {$source}
ln-prompt-replace = remplacer {$file} ?
ln-cannot-backup = impossible de sauvegarder {$file}
+3 -10
View File
@@ -353,22 +353,15 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
}
}
target_dir.to_path_buf()
} else if let Some(name) = srcpath.as_os_str().to_str() {
match Path::new(name).file_name() {
} else {
match srcpath.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 => target_dir.join(srcpath),
}
} else {
show_error!(
"{}",
translate!("ln-error-cannot-stat", "path" => srcpath.quote())
);
all_successful = false;
continue;
};
if linked_destinations.contains(&targetpath) {
+45
View File
@@ -378,6 +378,51 @@ fn test_symlink_target_dir() {
assert_eq!(at.resolve_link(file_b_link), file_b);
}
#[test]
#[cfg(target_os = "linux")]
fn test_symlink_target_dir_non_utf8_source_name() {
use std::ffi::OsStr;
use std::fs;
use std::os::unix::ffi::OsStrExt;
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let target_dir = "test_symlink_target_dir_non_utf8";
let source_bytes = b"source_\xFF\xFE";
let source_name = OsStr::from_bytes(source_bytes);
at.mkdir(target_dir);
at.touch(source_name);
scene
.ucmd()
.args(&["-s", "-t", target_dir])
.arg(source_name)
.succeeds()
.no_stderr();
let target_dir_path = at.plus(target_dir);
let mut dir_entries = fs::read_dir(&target_dir_path)
.expect("reading target directory entries after creating symlink");
let created_entry = dir_entries
.next()
.expect("finding created entry in target directory")
.expect("reading created target-directory entry");
assert!(
dir_entries.next().is_none(),
"expected only one created entry in target directory"
);
assert_eq!(
created_entry.file_name().as_os_str().as_bytes(),
source_bytes
);
let created_link_path = target_dir_path.join(source_name);
let created_link_target = fs::read_link(&created_link_path)
.expect("reading created symlink target in target directory");
assert_eq!(created_link_target.as_os_str().as_bytes(), source_bytes);
}
#[test]
fn test_symlink_target_dir_from_dir() {
let (at, mut ucmd) = at_and_ucmd!();