shadow-core: strip Rust's "(os error N)" suffix from strerror

io::Error::from_raw_os_error(e).to_string() renders as
"Permission denied (os error 13)" — Rust appends its own suffix to the
libc text. Strip it so the message is the bare OS string (matching
GNU/coreutils output), and add a regression test that the suffix is
absent. Addresses review feedback on #171.
This commit is contained in:
Pierre Warnier
2026-06-10 12:50:16 +02:00
parent 6ea1a48883
commit 15f8ecc505
+19 -1
View File
@@ -18,7 +18,16 @@
/// translate (musl), this is the untranslated English text.
#[must_use]
pub fn strerror(errno: i32) -> String {
std::io::Error::from_raw_os_error(errno).to_string()
// `io::Error::from_raw_os_error` carries libc's `strerror` text, but its
// `Display` appends Rust's own " (os error N)" suffix. Strip that so the
// result is the bare OS message (matching GNU/coreutils output). The
// suffix is emitted verbatim in English regardless of locale, so a
// localized message cannot contain it earlier in the string.
let rendered = std::io::Error::from_raw_os_error(errno).to_string();
match rendered.rfind(" (os error ") {
Some(cut) => rendered[..cut].to_string(),
None => rendered,
}
}
/// The OS message for `EACCES` ("Permission denied"), sourced from libc.
@@ -41,4 +50,13 @@ mod tests {
// Same value libc would give for EACCES directly.
assert_eq!(msg, strerror(libc::EACCES));
}
#[test]
fn strerror_drops_the_rust_os_error_suffix() {
// Regression guard: the bare OS message must not carry Rust's
// " (os error N)" rendering (the bug that made permission_denied()
// return "Permission denied (os error 13)").
let msg = strerror(libc::EACCES);
assert!(!msg.contains("(os error"), "got: {msg:?}");
}
}