From 3784e8667dedf1aec06567fe862ecb1963b7918d Mon Sep 17 00:00:00 2001 From: emirmrkaljevic <93012621+emirmrkaljevic@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:16:31 +0200 Subject: [PATCH 1/2] sort: report read errors from the --check and --merge reader threads --- src/uu/sort/src/check.rs | 9 +++++++-- src/uu/sort/src/merge.rs | 2 ++ tests/by-util/test_sort.rs | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/check.rs b/src/uu/sort/src/check.rs index a826bc75507..8ed955384d0 100644 --- a/src/uu/sort/src/check.rs +++ b/src/uu/sort/src/check.rs @@ -37,7 +37,7 @@ pub fn check(path: &OsStr, settings: &GlobalSettings) -> UResult<()> { let file = open(path)?; let (recycled_sender, recycled_receiver) = sync_channel(2); let (loaded_sender, loaded_receiver) = sync_channel(2); - thread::spawn({ + let reader_join_handle = thread::spawn({ let settings = settings.clone(); move || reader(file, &recycled_receiver, &loaded_sender, &settings) }); @@ -111,7 +111,10 @@ pub fn check(path: &OsStr, settings: &GlobalSettings) -> UResult<()> { drop(recycled_sender); while loaded_receiver.recv().is_ok() {} - result + // A read error ends the input early, so without this the part read so far + // would be checked and reported as sorted. + let reader_result = reader_join_handle.join().unwrap(); + result.and(reader_result) } /// The function running on the reader thread. @@ -121,6 +124,8 @@ fn reader( sender: &SyncSender, settings: &GlobalSettings, ) -> UResult<()> { + // Error messages are built on this thread, and localization is per thread. + let _ = uucore::locale::setup_localization("sort"); let mut carry_over = vec![]; for recycled_chunk in receiver { let should_continue = chunks::read( diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index bfbd7fa64f1..62fe32c59a1 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -256,6 +256,8 @@ fn reader( settings: &GlobalSettings, separator: u8, ) -> UResult<()> { + // Error messages are built on this thread, and localization is per thread. + let _ = uucore::locale::setup_localization("sort"); for (file_idx, recycled_chunk) in recycled_receiver { if let Some(ReaderFile { file, diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 23dd109dd12..47740888b76 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -2335,6 +2335,24 @@ fn test_files0_from_non_utf8_filename() { .stderr_contains("sort: cannot read"); } +#[rstest::rstest] +#[case(&["-c", "dir"])] +#[case(&["-C", "dir"])] +#[case(&["-m", "input", "dir"])] +#[cfg(target_os = "linux")] +fn test_read_error_on_reader_thread(#[case] args: &[&str]) { + // `--check` used to exit 0 without a message when its input could not be + // read, and `--merge` printed the untranslated message id. + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("dir"); + at.write("input", "a\nb\n"); + ucmd.args(args) + .fails_with_code(2) + .stderr_contains("sort: read failed: ") + .stderr_contains("Is a directory") + .stderr_does_not_contain("sort-read-failed"); +} + #[test] #[cfg(unix)] fn test_files0_from_unreadable_source() { From b73553f8df0f9ff8f03616544cd6f13c6c3f0c95 Mon Sep 17 00:00:00 2001 From: emirmrkaljevic <93012621+emirmrkaljevic@users.noreply.github.com> Date: Fri, 23 Oct 2026 08:55:23 +0200 Subject: [PATCH 2/2] sort: return SortError::ChunkReadFailed from chunks::read --- src/uu/sort/src/check.rs | 2 -- src/uu/sort/src/chunks.rs | 14 ++++++-------- src/uu/sort/src/merge.rs | 2 -- src/uu/sort/src/sort.rs | 3 +++ tests/by-util/test_sort.rs | 2 -- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/uu/sort/src/check.rs b/src/uu/sort/src/check.rs index 8ed955384d0..118d2d0fa6a 100644 --- a/src/uu/sort/src/check.rs +++ b/src/uu/sort/src/check.rs @@ -124,8 +124,6 @@ fn reader( sender: &SyncSender, settings: &GlobalSettings, ) -> UResult<()> { - // Error messages are built on this thread, and localization is per thread. - let _ = uucore::locale::setup_localization("sort"); let mut carry_over = vec![]; for recycled_chunk in receiver { let should_continue = chunks::read( diff --git a/src/uu/sort/src/chunks.rs b/src/uu/sort/src/chunks.rs index ee8261dd856..1a2d16da6f8 100644 --- a/src/uu/sort/src/chunks.rs +++ b/src/uu/sort/src/chunks.rs @@ -18,10 +18,11 @@ use std::{ use memchr::memchr_iter; use self_cell::self_cell; -use uucore::error::{UResult, USimpleError, strip_errno}; -use uucore::translate; +use uucore::error::UResult; -use crate::{GeneralBigDecimalParseResult, GlobalSettings, Line, numeric_str_cmp::NumInfo}; +use crate::{ + GeneralBigDecimalParseResult, GlobalSettings, Line, SortError, numeric_str_cmp::NumInfo, +}; const ALLOC_CHUNK_SIZE: usize = 64 * 1024; const MAX_TOKEN_BUFFER_BYTES: usize = 4 * 1024 * 1024; @@ -424,11 +425,8 @@ fn read_to_buffer( Err(e) if e.kind() == ErrorKind::Interrupted => { // retry } - Err(e) => { - return Err(USimpleError::new( - 2, - translate!("sort-read-failed", "error" => strip_errno(&e)), - )); + Err(error) => { + return Err(SortError::ChunkReadFailed { error }.into()); } } } diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index 62fe32c59a1..bfbd7fa64f1 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -256,8 +256,6 @@ fn reader( settings: &GlobalSettings, separator: u8, ) -> UResult<()> { - // Error messages are built on this thread, and localization is per thread. - let _ = uucore::locale::setup_localization("sort"); for (file_idx, recycled_chunk) in recycled_receiver { if let Some(ReaderFile { file, diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 94edeaa8ca2..f035d79e685 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -161,6 +161,9 @@ pub enum SortError { error: std::io::Error, }, + #[error("{}", translate!("sort-read-failed", "error" => strip_errno(.error)))] + ChunkReadFailed { error: std::io::Error }, + #[error("{}", translate!("sort-open-tmp-file-failed", "error" => strip_errno(.error)))] OpenTmpFileFailed { error: std::io::Error }, diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 47740888b76..12fb8a84941 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -2341,8 +2341,6 @@ fn test_files0_from_non_utf8_filename() { #[case(&["-m", "input", "dir"])] #[cfg(target_os = "linux")] fn test_read_error_on_reader_thread(#[case] args: &[&str]) { - // `--check` used to exit 0 without a message when its input could not be - // read, and `--merge` printed the untranslated message id. let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("dir"); at.write("input", "a\nb\n");