diff --git a/programs/fileio.c b/programs/fileio.c index 3c638f37d..4d579d5cf 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -351,6 +351,7 @@ enum { FIO_RUST_OPEN_SRC_STAT_FAILED = 1, FIO_RUST_OPEN_SRC_NON_REGULAR = 2, FIO_RUST_OPEN_SRC_FOPEN_FAILED = 3, + FIO_RUST_OPEN_SRC_STDIN = 4, }; int FIO_rust_openSrcFile(int allowBlockDevices, const char* srcFileName, @@ -671,17 +672,17 @@ static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFile int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0; assert(srcFileName != NULL); assert(statbuf != NULL); - if (!strcmp (srcFileName, stdinmark)) { - DISPLAYLEVEL(4,"Using stdin for input \n"); - SET_BINARY_MODE(stdin); - return stdin; - } - { FILE* f = NULL; int const status = FIO_rust_openSrcFile(allowBlockDevices, srcFileName, statbuf, &f); + if (status == FIO_RUST_OPEN_SRC_STDIN) { + DISPLAYLEVEL(4,"Using stdin for input \n"); + SET_BINARY_MODE(stdin); + return stdin; + } + if (status == FIO_RUST_OPEN_SRC_STAT_FAILED) { DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n", srcFileName, strerror(errno)); diff --git a/rust/src/fileio_backend.rs b/rust/src/fileio_backend.rs index 52665e59c..eb69e7881 100644 --- a/rust/src/fileio_backend.rs +++ b/rust/src/fileio_backend.rs @@ -55,6 +55,7 @@ const FIO_OPEN_SRC_SUCCESS: c_int = 0; const FIO_OPEN_SRC_STAT_FAILED: c_int = 1; const FIO_OPEN_SRC_NON_REGULAR: c_int = 2; const FIO_OPEN_SRC_FOPEN_FAILED: c_int = 3; +const FIO_OPEN_SRC_STDIN: c_int = 4; const FIO_OPEN_DST_SUCCESS: c_int = 0; const FIO_OPEN_DST_SETBUF_FAILED: c_int = 1; @@ -84,6 +85,7 @@ const FIO_OPEN_DST_ACTION_SUCCESS: c_int = 11; const FIO_OPEN_DST_ACTION_INVALID: c_int = 12; static STDOUT_MARK: &[u8] = b"/*stdout*\\\0"; +static STDIN_MARK: &[u8] = b"/*stdin*\\\0"; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum DestinationDecision { @@ -475,7 +477,8 @@ pub unsafe extern "C" fn FIO_rust_getDictFileStat( FIO_DICT_STAT_SUCCESS } -/// Opens a non-stdin source path and returns a status for the C policy adapter. +/// Classifies stdin before opening or statting, then opens a non-stdin source +/// path and returns a status for the C policy adapter. /// /// `stat_buf` and `out_file` deliberately cross this boundary as opaque /// pointers. The existing utility ABI owns the target-specific `stat_t` layout @@ -488,7 +491,17 @@ pub unsafe extern "C" fn FIO_rust_openSrcFile( stat_buf: *mut c_void, out_file: *mut *mut c_void, ) -> c_int { - if file_name.is_null() || stat_buf.is_null() || out_file.is_null() { + if file_name.is_null() || out_file.is_null() { + return FIO_OPEN_SRC_STAT_FAILED; + } + + // The stdin sentinel is a dispatch decision, not a filesystem path. It + // must be recognized before the stat buffer is touched; C retains the + // stream selection, binary-mode setup, and exact diagnostic. + if c_string_equals(file_name, STDIN_MARK) { + return FIO_OPEN_SRC_STDIN; + } + if stat_buf.is_null() { return FIO_OPEN_SRC_STAT_FAILED; } @@ -1698,6 +1711,19 @@ mod tests { assert!(file.is_null()); } + #[test] + fn classifies_stdin_before_stat_and_leaves_output_untouched() { + let stdin = CString::new("/*stdin*\\").expect("stdin sentinel contains no NUL"); + let sentinel = ptr::dangling_mut::(); + let mut out_file = sentinel; + + let status = unsafe { + FIO_rust_openSrcFile(0, stdin.as_ptr(), ptr::null_mut(), &mut out_file) + }; + assert_eq!(status, FIO_OPEN_SRC_STDIN); + assert_eq!(out_file, sentinel); + } + #[test] fn rejects_directory_source_path() { let path = temp_path("open-directory");