Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .vscode/cspell.dictionaries/jargon.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ denoland
deque
dequeue
dev
EINTR
eintr
nextest
SIGUSR
nonprinting
multibyte
devs
discoverability
duplicative
Expand Down
13 changes: 13 additions & 0 deletions .vscode/cspell.dictionaries/shell.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ mountinfo
mountpoint
mtab
nullglob

# * Signals
SIGUSR
SIGUSR1
SIGUSR2
SIGINT
SIGTERM
SIGKILL
SIGSTOP
SIGCONT
SIGPIPE
SIGALRM
SIGCHLD
passwd
pipefail
popd
Expand Down
13 changes: 13 additions & 0 deletions .vscode/cspell.dictionaries/workspace.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ advapi32-sys
aho-corasick
backtrace
blake2b_simd

# * uutils project
uutils
coreutils
uucore
uutests
ucmd
uumain
rlimit
mkfifo
urandom
uchild
ello
bstr
bytecount
byteorder
Expand Down
12 changes: 8 additions & 4 deletions src/uu/cat/src/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ fn write_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
.write_all(&buf[..n])
.inspect_err(handle_broken_pipe)?;
}
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
}
}
Expand All @@ -545,10 +546,13 @@ fn write_lines<R: FdReadable>(
// Add a 32K buffer for stdout - this greatly improves performance.
let mut writer = BufWriter::with_capacity(32 * 1024, stdout);

while let Ok(n) = handle.reader.read(&mut in_buf) {
if n == 0 {
break;
}
loop {
let n = match handle.reader.read(&mut in_buf) {
Ok(0) => break,
Ok(n) => n,
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
};
let in_buf = &in_buf[..n];
let mut pos = 0;
while pos < n {
Expand Down
20 changes: 18 additions & 2 deletions src/uu/comm/src/comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,24 @@ pub fn are_files_identical(path1: &Path, path2: &Path) -> io::Result<bool> {
let mut buffer2 = [0; 8192];

loop {
let bytes1 = reader1.read(&mut buffer1)?;
let bytes2 = reader2.read(&mut buffer2)?;
// Read from first file with EINTR retry handling
// This loop retries the read operation if it's interrupted by signals (e.g., SIGUSR1)
// instead of failing, which is the POSIX-compliant way to handle interrupted I/O
let bytes1 = loop {
match reader1.read(&mut buffer1) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
result => break result?,
}
};

// Read from second file with EINTR retry handling
// Same retry logic as above for the second file to ensure consistent behavior
let bytes2 = loop {
match reader2.read(&mut buffer2) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
result => break result?,
}
};

if bytes1 != bytes2 {
return Ok(false);
Expand Down
43 changes: 29 additions & 14 deletions src/uu/od/src/partial_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,48 @@ impl<R: Read> Read for PartialReader<R> {
while self.skip > 0 {
let skip_count: usize = cmp::min(self.skip as usize, MAX_SKIP_BUFFER);

match self.inner.read(&mut bytes[..skip_count])? {
0 => {
// this is an error as we still have more to skip
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
translate!("od-error-skip-past-end"),
));
loop {
match self.inner.read(&mut bytes[..skip_count]) {
Ok(0) => {
// this is an error as we still have more to skip
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
translate!("od-error-skip-past-end"),
));
}
Ok(n) => {
self.skip -= n as u64;
break;
}
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
n => self.skip -= n as u64,
}
}
}

match self.limit {
None => self.inner.read(out),
None => loop {
match self.inner.read(out) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
result => return result,
}
},
Some(0) => Ok(0),
Some(ref mut limit) => {
let slice = if *limit > (out.len() as u64) {
out
} else {
&mut out[0..(*limit as usize)]
};
match self.inner.read(slice) {
Err(e) => Err(e),
Ok(r) => {
*limit -= r as u64;
Ok(r)
loop {
match self.inner.read(slice) {
Ok(r) => {
*limit -= r as u64;
return Ok(r);
}
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
}
}
Expand Down
57 changes: 57 additions & 0 deletions tests/by-util/test_cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,3 +826,60 @@ fn test_child_when_pipe_in() {

ts.ucmd().pipe_in("content").run().stdout_is("content");
}

#[test]
fn test_cat_eintr_handling() {
// Test that cat properly handles EINTR (ErrorKind::Interrupted) during I/O operations
// This verifies the signal interruption retry logic added in the EINTR handling fix
use std::io::{Error, ErrorKind, Read};
use std::sync::{Arc, Mutex};

// Create a mock reader that simulates EINTR interruptions
struct InterruptedReader {
data: Vec<u8>,
position: usize,
interrupt_count: Arc<Mutex<usize>>,
}

impl Read for InterruptedReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
// Simulate interruption on first read attempt
if self.position < self.data.len() {
let mut count = self.interrupt_count.lock().unwrap();
if *count == 0 {
*count += 1;
return Err(Error::new(
ErrorKind::Interrupted,
"Simulated signal interruption",
));
}
}

// Return actual data on subsequent attempts
if self.position >= self.data.len() {
return Ok(0);
}

let remaining = self.data.len() - self.position;
let to_copy = std::cmp::min(buf.len(), remaining);
buf[..to_copy].copy_from_slice(&self.data[self.position..self.position + to_copy]);
self.position += to_copy;
Ok(to_copy)
}
}

let test_data = b"Hello, World!\n";
let interrupt_count = Arc::new(Mutex::new(0));
let reader = InterruptedReader {
data: test_data.to_vec(),
position: 0,
interrupt_count: interrupt_count.clone(),
};

// Test that cat can handle the interrupted reader
let result = std::io::copy(&mut { reader }, &mut std::io::stdout());
assert!(result.is_ok());

// Verify that the interruption was encountered and handled
assert_eq!(*interrupt_count.lock().unwrap(), 1);
}
38 changes: 38 additions & 0 deletions tests/by-util/test_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,41 @@ fn comm_emoji_sorted_inputs() {
.succeeds()
.stdout_only("💐\n\t\t🦀\n\t🪽\n");
}

#[test]
fn test_comm_eintr_handling() {
// Test that comm properly handles EINTR (ErrorKind::Interrupted) during file comparison
// This verifies the signal interruption retry logic in are_files_identical function
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

// Create test files with identical content
let test_content = "line1\nline2\nline3\n";
at.write("file1", test_content);
at.write("file2", test_content);

// Test that comm can handle interrupted reads during file comparison
// The EINTR handling should retry and complete successfully
scene
.ucmd()
.args(&["file1", "file2"])
.succeeds()
.stdout_contains("line1") // Check that content is present (comm adds tabs for identical lines)
.stdout_contains("line2")
.stdout_contains("line3");

// Create test files with identical content
let test_content = "line1\nline2\nline3\n";
at.write("file1", test_content);
at.write("file2", test_content);

// Test that comm can handle interrupted reads during file comparison
// The EINTR handling should retry and complete successfully
scene
.ucmd()
.args(&["file1", "file2"])
.succeeds()
.stdout_contains("line1") // Check that content is present (comm adds tabs for identical lines)
.stdout_contains("line2")
.stdout_contains("line3");
}
Loading
Loading