Skip to content

Commit 06d22f4

Browse files
committed
comm: fix false sorting warnings when reading from FIFOs/process substitution
1 parent 6dac4d4 commit 06d22f4

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

src/uu/comm/src/comm.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ pub fn are_files_identical(path1: &Path, path2: &Path) -> io::Result<bool> {
132132
let metadata1 = metadata(path1)?;
133133
let metadata2 = metadata(path2)?;
134134

135+
// Only compare contents for regular files.
136+
// For FIFOs, pipes, and other special files, reading would consume data
137+
// that we need for the actual comparison. Return false to enable order checking.
138+
// See: https://bugs.launchpad.net/ubuntu/+source/rust-coreutils/+bug/2138315
139+
if !metadata1.file_type().is_file() || !metadata2.file_type().is_file() {
140+
return Ok(false);
141+
}
142+
135143
if metadata1.len() != metadata2.len() {
136144
return Ok(false);
137145
}

tests/by-util/test_comm.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,71 @@ fn comm_emoji_sorted_inputs() {
611611
.stdout_only("💐\n\t\t🦀\n\t🪽\n");
612612
}
613613

614+
/// Regression test for https://bugs.launchpad.net/ubuntu/+source/rust-coreutils/+bug/2138315
615+
/// When reading from FIFOs (like process substitution `<(cat file)`), comm would falsely report
616+
/// "file is not in sorted order" even when input was properly sorted.
617+
#[test]
618+
#[cfg(unix)]
619+
fn test_large_sorted_input_from_fifo_no_false_sorting_warning() {
620+
use std::fmt::Write as _;
621+
use std::fs::OpenOptions;
622+
use std::io::Write as _;
623+
use std::thread;
624+
625+
let scene = TestScenario::new(util_name!());
626+
let at = &scene.fixtures;
627+
628+
// Create two FIFOs to simulate process substitution
629+
at.mkfifo("fifo_a");
630+
at.mkfifo("fifo_b");
631+
632+
// Content for the FIFOs
633+
let content_a = "00000\n".to_string();
634+
// Create a large sorted file (2000 lines of zero-padded numbers)
635+
// This is well above the ~1365 line threshold where the bug manifests
636+
let content_b: String = (1..=2000).fold(String::new(), |mut acc, n| {
637+
writeln!(acc, "{n:05}").unwrap();
638+
acc
639+
});
640+
641+
// Start comm reading from both FIFOs (non-blocking)
642+
let proc = scene
643+
.ucmd()
644+
.args(&["-23", "fifo_a", "fifo_b"])
645+
.run_no_wait();
646+
647+
// Spawn threads to write to the FIFOs
648+
let fifo_a_path = at.plus("fifo_a");
649+
let fifo_b_path = at.plus("fifo_b");
650+
651+
let thread_a = thread::spawn(move || {
652+
let mut pipe = OpenOptions::new()
653+
.write(true)
654+
.create(false)
655+
.open(fifo_a_path)
656+
.unwrap();
657+
pipe.write_all(content_a.as_bytes()).unwrap();
658+
});
659+
660+
let thread_b = thread::spawn(move || {
661+
let mut pipe = OpenOptions::new()
662+
.write(true)
663+
.create(false)
664+
.open(fifo_b_path)
665+
.unwrap();
666+
pipe.write_all(content_b.as_bytes()).unwrap();
667+
});
668+
669+
// Wait for everything to complete
670+
let result = proc.wait().unwrap();
671+
thread_a.join().unwrap();
672+
thread_b.join().unwrap();
673+
674+
// Should succeed with exit code 0 and output "00000\n"
675+
// Before the fix, this would fail with "file 2 is not in sorted order"
676+
result.success().stdout_only("00000\n");
677+
}
678+
614679
#[test]
615680
fn test_comm_eintr_handling() {
616681
// Test that comm properly handles EINTR (ErrorKind::Interrupted) during file comparison

0 commit comments

Comments
 (0)