From e49c5ad878293fe3eec47004af5850d7b6e779f9 Mon Sep 17 00:00:00 2001 From: Pablo Garcia Date: Mon, 17 Aug 2026 22:53:54 +0200 Subject: [PATCH] sort: strip the errno suffix from read errors read_to_buffer built its error with e.to_string(), which carries the "(os error N)" tail that GNU does not print. Every other io error in sort already goes through strip_errno. Fixes #13992 --- src/uu/sort/src/chunks.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/chunks.rs b/src/uu/sort/src/chunks.rs index 4b388dc7bbd..f5d6a111a6d 100644 --- a/src/uu/sort/src/chunks.rs +++ b/src/uu/sort/src/chunks.rs @@ -17,7 +17,7 @@ use std::{ use memchr::memchr_iter; use self_cell::self_cell; -use uucore::error::{UResult, USimpleError}; +use uucore::error::{UResult, USimpleError, strip_errno}; use crate::{ GeneralBigDecimalParseResult, GlobalSettings, Line, SortMode, numeric_str_cmp::NumInfo, @@ -427,7 +427,7 @@ fn read_to_buffer( Err(e) if e.kind() == ErrorKind::Interrupted => { // retry } - Err(e) => return Err(USimpleError::new(2, e.to_string())), + Err(e) => return Err(USimpleError::new(2, strip_errno(&e))), } } } @@ -460,3 +460,37 @@ pub fn parse_into_chunk<'a>( line_count_hint, } } + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Error; + + struct FailingReader; + + impl Read for FailingReader { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Err(Error::from_raw_os_error(5)) + } + } + + #[test] + fn read_error_message_has_no_errno_suffix() { + let mut buffer = vec![0u8; 64]; + let mut next_files = std::iter::empty::>(); + let err = read_to_buffer( + &mut FailingReader, + &mut next_files, + &mut buffer, + None, + 0, + b'\n', + ) + .unwrap_err(); + let msg = err.to_string(); + assert!(!msg.contains("(os error"), "leaked errno: {msg}"); + assert_eq!(msg, strip_errno(&Error::from_raw_os_error(5))); + // Sanity: an error without an errno is untouched. + assert_eq!(strip_errno(&Error::other("custom")), "custom"); + } +}