From 75629e7db4e09ce944d376d22d2991d520a3bc43 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 14:45:00 +0100 Subject: [PATCH 01/10] `bzip2.rs`: add tests for the `-t` test flag --- tests/quick.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) diff --git a/tests/quick.rs b/tests/quick.rs index 2943c1d63..03332b248 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -679,7 +679,7 @@ fn flags_after_double_dash() { } #[test] -fn compress_and_test() { +fn stdout_and_test() { let mut cmd = command(); cmd.args(["-c", "-t"]); let output = cmd.output().unwrap(); @@ -830,7 +830,7 @@ fn cannot_guess_file_name() { } #[test] -fn input_file_does_not_exist() { +fn decompress_input_file_does_not_exist() { let tmpdir = tempfile::tempdir().unwrap(); let sample1 = tmpdir.path().join("sample1"); @@ -860,7 +860,7 @@ fn input_file_does_not_exist() { } #[test] -fn input_file_is_a_directory() { +fn decompress_input_file_is_a_directory() { let tmpdir = tempfile::tempdir().unwrap(); let mut cmd = command(); @@ -1140,3 +1140,176 @@ fn input_file_is_not_bzip2_data() { ), ); } + +#[test] +fn test_valid_stdin() { + use std::io::Write; + + let compressed = include_bytes!("input/quick/sample1.bz2"); + + let mut cmd = command(); + + // Set up command to read from stdin, decompress, and output to stdout + let mut child = cmd + .arg("-t") + .arg("-v") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to start child process"); + + // Write the compressed data to stdin + if let Some(mut stdin) = child.stdin.take() { + stdin + .write_all(compressed) + .expect("Failed to write to stdin"); + } + + // Wait for the child process to finish and capture output + let output = child.wait_with_output().expect("Failed to read stdout"); + + assert!( + output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + " (stdin): ok\n", + ); +} + +#[test] +fn test_invalid_stdin() { + use std::io::Write; + + let mut cmd = command(); + + // Set up command to read from stdin, decompress, and output to stdout + let mut child = cmd + .arg("-t") + .arg("-v") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to start child process"); + + // Write the random data to stdin + if let Some(mut stdin) = child.stdin.take() { + stdin + .write_all(b"fee stjert, sibben stjerre") + .expect("Failed to write to stdin"); + } + + // Wait for the child process to finish and capture output + let output = child.wait_with_output().expect("Failed to read stdout"); + + assert!( + !output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + concat!( + " (stdin): bad magic number (file not created by bzip2)\n", + "\n", + "You can use the `bzip2recover' program to attempt to recover\n", + "data from undamaged sections of corrupted files.\n", + "\n" + ), + ); +} + +#[test] +fn test_file_to_file_bz2() { + let tmpdir = tempfile::tempdir().unwrap(); + let sample1 = tmpdir.path().join("sample1.bz2"); + + std::fs::copy("tests/input/quick/sample1.bz2", &sample1).unwrap(); + + let mut cmd = command(); + + let output = match cmd.arg("-t").arg(&sample1).output() { + Ok(output) => output, + Err(err) => panic!("Running {cmd:?} failed with {err:?}"), + }; + + assert!( + output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); +} + +#[test] +fn test_input_file_does_not_exist() { + let tmpdir = tempfile::tempdir().unwrap(); + let sample1 = tmpdir.path().join("sample1"); + + let mut cmd = command(); + + let output = match cmd.arg("-t").arg("-vvv").arg(&sample1).output() { + Ok(output) => output, + Err(err) => panic!("Running {cmd:?} failed with {err:?}"), + }; + + assert!( + !output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + format!( + "bzip2: Can't open input {in_file}: No such file or directory.\n", + in_file = sample1.display(), + ), + ); +} + +#[test] +fn test_input_file_is_a_directory() { + let tmpdir = tempfile::tempdir().unwrap(); + + let mut cmd = command(); + + let output = match cmd.arg("-t").arg("-vvv").arg(tmpdir.path()).output() { + Ok(output) => output, + Err(err) => panic!("Running {cmd:?} failed with {err:?}"), + }; + + assert!( + !output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + format!( + "bzip2: Input file {in_file} is a directory.\n", + in_file = tmpdir.path().display(), + ), + ); +} From bb2327634fa35c28b41c6c4d74b03746c2111b6d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 14:50:40 +0100 Subject: [PATCH 02/10] `fn testf`: accept a `Option` --- bzip2.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index 74cd5cc2d..4dc9d2ed1 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -1923,28 +1923,32 @@ unsafe fn uncompress(name: Option) { }; } -unsafe fn testf(name: *mut c_char) { +unsafe fn testf(name: Option) { let inStr: *mut FILE; delete_output_on_interrupt = false; - if name.is_null() && srcMode != SourceMode::I2O { - panic(b"testf: bad modes\n\0" as *const u8 as *const libc::c_char); - } + copyFileName( outName.as_mut_ptr(), b"(none)\0" as *const u8 as *const libc::c_char, ); - match srcMode { - SourceMode::I2O => { + + match (name, srcMode) { + (_, SourceMode::I2O) => { copyFileName( inName.as_mut_ptr(), b"(stdin)\0" as *const u8 as *const libc::c_char, ); } - SourceMode::F2O => { - copyFileName(inName.as_mut_ptr(), name); + (Some(name), SourceMode::F2O) => { + let name = CString::new(name).unwrap(); + copyFileName(inName.as_mut_ptr(), name.as_ptr()); } - SourceMode::F2F => { - copyFileName(inName.as_mut_ptr(), name); + (Some(name), SourceMode::F2F) => { + let name = CString::new(name).unwrap(); + copyFileName(inName.as_mut_ptr(), name.as_ptr()); + } + (None, SourceMode::F2O | SourceMode::F2F) => { + panic_str("testf: bad modes"); } } if srcMode != SourceMode::I2O && contains_dubious_chars(inName.as_mut_ptr()) { @@ -2364,7 +2368,7 @@ unsafe fn main_0(program_path: &Path) -> IntNative { OperationMode::Test => { test_fails_exists = false; if srcMode == SourceMode::I2O { - testf(std::ptr::null_mut()); + testf(None); } else { decode = true; for name in arg_list { @@ -2372,8 +2376,7 @@ unsafe fn main_0(program_path: &Path) -> IntNative { decode = false; } else if !(name.starts_with('-') && decode) { numFilesProcessed += 1; - let name = CString::new(name).unwrap(); - testf(name.as_ptr().cast_mut()); + testf(Some(name)); } } } From 748dc4a82bbe59d4d3d609caac61c6531a713187 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 14:54:52 +0100 Subject: [PATCH 03/10] `fn testf`: remove `fprintf` --- bzip2.rs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index 4dc9d2ed1..7d6993368 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -1951,13 +1951,13 @@ unsafe fn testf(name: Option) { panic_str("testf: bad modes"); } } + if srcMode != SourceMode::I2O && contains_dubious_chars(inName.as_mut_ptr()) { if noisy { - fprintf( - stderr, - b"%s: There are no files matching `%s'.\n\0" as *const u8 as *const libc::c_char, - progName, - inName.as_mut_ptr(), + eprintln!( + "{}: There are no files matching `{}'.", + get_program_name().display(), + CStr::from_ptr(inName.as_ptr()).to_string_lossy(), ); } setExit(1 as libc::c_int); @@ -1966,7 +1966,7 @@ unsafe fn testf(name: Option) { if srcMode != SourceMode::I2O && fileExists(inName.as_mut_ptr()) == 0 { eprintln!( "{}: Can't open input {}: {}.", - std::env::args().next().unwrap(), + get_program_name().display(), CStr::from_ptr(inName.as_ptr()).to_string_lossy(), display_last_os_error(), ); @@ -1977,11 +1977,10 @@ unsafe fn testf(name: Option) { let mut statBuf: stat = zeroed(); stat(inName.as_mut_ptr(), &mut statBuf); if statBuf.st_mode & 0o170000 == 0o40000 { - fprintf( - stderr, - b"%s: Input file %s is a directory.\n\0" as *const u8 as *const libc::c_char, - progName, - inName.as_mut_ptr(), + eprintln!( + "{}: Input file {} is a directory.", + get_program_name().display(), + CStr::from_ptr(inName.as_ptr()).to_string_lossy(), ); setExit(1 as libc::c_int); return; @@ -1990,17 +1989,14 @@ unsafe fn testf(name: Option) { match srcMode { SourceMode::I2O => { if isatty(fileno(stdin)) != 0 { - fprintf( - stderr, - b"%s: I won't read compressed data from a terminal.\n\0" as *const u8 - as *const libc::c_char, - progName, + eprintln!( + "{}: I won't read compressed data from a terminal.", + get_program_name().display(), ); - fprintf( - stderr, - b"%s: For help, type: `%s --help'.\n\0" as *const u8 as *const libc::c_char, - progName, - progName, + eprintln!( + "{}: For help, type: `{} --help'.", + get_program_name().display(), + get_program_name().display(), ); setExit(1 as libc::c_int); return; @@ -2015,7 +2011,7 @@ unsafe fn testf(name: Option) { if inStr.is_null() { eprintln!( "{}: Can't open input file {}:{}.", - std::env::args().next().unwrap(), + get_program_name().display(), CStr::from_ptr(inName.as_ptr()).to_string_lossy(), display_last_os_error(), ); @@ -2025,18 +2021,14 @@ unsafe fn testf(name: Option) { } } if verbosity >= 1 as libc::c_int { - fprintf( - stderr, - b" %s: \0" as *const u8 as *const libc::c_char, - inName.as_mut_ptr(), - ); + eprint!(" {}: ", CStr::from_ptr(inName.as_ptr()).to_string_lossy()); pad(inName.as_mut_ptr()); fflush(stderr); } outputHandleJustInCase = std::ptr::null_mut::(); let allOK = testStream(inStr); if allOK as libc::c_int != 0 && verbosity >= 1 as libc::c_int { - fprintf(stderr, b"ok\n\0" as *const u8 as *const libc::c_char); + eprintln!("ok"); } if allOK == 0 { test_fails_exists = true; From 8fda5474569cb7454d7e1ca6d4a72366c99e3379 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:02:35 +0100 Subject: [PATCH 04/10] `fn testf`: start using `Path` instead of `CStr` --- bzip2.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index 7d6993368..ac7b87cd4 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -1932,18 +1932,23 @@ unsafe fn testf(name: Option) { b"(none)\0" as *const u8 as *const libc::c_char, ); + let in_name; + match (name, srcMode) { (_, SourceMode::I2O) => { + in_name = PathBuf::from("(stdin)"); copyFileName( inName.as_mut_ptr(), b"(stdin)\0" as *const u8 as *const libc::c_char, ); } (Some(name), SourceMode::F2O) => { + in_name = PathBuf::from(&name); let name = CString::new(name).unwrap(); copyFileName(inName.as_mut_ptr(), name.as_ptr()); } (Some(name), SourceMode::F2F) => { + in_name = PathBuf::from(&name); let name = CString::new(name).unwrap(); copyFileName(inName.as_mut_ptr(), name.as_ptr()); } @@ -1952,39 +1957,35 @@ unsafe fn testf(name: Option) { } } - if srcMode != SourceMode::I2O && contains_dubious_chars(inName.as_mut_ptr()) { + if srcMode != SourceMode::I2O && contains_dubious_chars_safe(&in_name) { if noisy { eprintln!( "{}: There are no files matching `{}'.", get_program_name().display(), - CStr::from_ptr(inName.as_ptr()).to_string_lossy(), + in_name.display(), ); } setExit(1 as libc::c_int); return; } - if srcMode != SourceMode::I2O && fileExists(inName.as_mut_ptr()) == 0 { + if srcMode != SourceMode::I2O && !in_name.exists() { eprintln!( "{}: Can't open input {}: {}.", get_program_name().display(), - CStr::from_ptr(inName.as_ptr()).to_string_lossy(), + in_name.display(), display_last_os_error(), ); setExit(1 as libc::c_int); return; } - if srcMode != SourceMode::I2O { - let mut statBuf: stat = zeroed(); - stat(inName.as_mut_ptr(), &mut statBuf); - if statBuf.st_mode & 0o170000 == 0o40000 { - eprintln!( - "{}: Input file {} is a directory.", - get_program_name().display(), - CStr::from_ptr(inName.as_ptr()).to_string_lossy(), - ); - setExit(1 as libc::c_int); - return; - } + if srcMode != SourceMode::I2O && in_name.is_dir() { + eprintln!( + "{}: Input file {} is a directory.", + get_program_name().display(), + in_name.display(), + ); + setExit(1 as libc::c_int); + return; } match srcMode { SourceMode::I2O => { @@ -2012,7 +2013,7 @@ unsafe fn testf(name: Option) { eprintln!( "{}: Can't open input file {}:{}.", get_program_name().display(), - CStr::from_ptr(inName.as_ptr()).to_string_lossy(), + in_name.display(), display_last_os_error(), ); setExit(1 as libc::c_int); @@ -2021,7 +2022,7 @@ unsafe fn testf(name: Option) { } } if verbosity >= 1 as libc::c_int { - eprint!(" {}: ", CStr::from_ptr(inName.as_ptr()).to_string_lossy()); + eprint!(" {}: ", in_name.display()); pad(inName.as_mut_ptr()); fflush(stderr); } From 94e9081cc5f05d39030c305c95cd5cfe425eaf82 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:08:46 +0100 Subject: [PATCH 05/10] `fn testf`: add test for checking multiple files --- tests/quick.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/quick.rs b/tests/quick.rs index 03332b248..962bdbb80 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -1232,7 +1232,7 @@ fn test_invalid_stdin() { } #[test] -fn test_file_to_file_bz2() { +fn test_file() { let tmpdir = tempfile::tempdir().unwrap(); let sample1 = tmpdir.path().join("sample1.bz2"); @@ -1255,6 +1255,43 @@ fn test_file_to_file_bz2() { assert!(output.stdout.is_empty()); } +#[test] +fn test_files() { + let tmpdir = tempfile::tempdir().unwrap(); + let sample1 = tmpdir.path().join("sample1.bz2"); + let longer = tmpdir.path().join("a_longer_file_name.bz2"); + + std::fs::copy("tests/input/quick/sample1.bz2", &sample1).unwrap(); + std::fs::copy("tests/input/quick/sample1.bz2", &longer).unwrap(); + + let mut cmd = command(); + + let output = match cmd.arg("-t").arg("-v").arg(&sample1).arg(&longer).output() { + Ok(output) => output, + Err(err) => panic!("Running {cmd:?} failed with {err:?}"), + }; + + assert!( + output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + format!( + concat!( + " {in_dir}/sample1.bz2: ok\n", + " {in_dir}/a_longer_file_name.bz2: ok\n", + ), + in_dir = tmpdir.path().display(), + ) + ); +} + #[test] fn test_input_file_does_not_exist() { let tmpdir = tempfile::tempdir().unwrap(); From ac85174a8617531512bf972e9b9a37de95a93afe Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:21:07 +0100 Subject: [PATCH 06/10] `fn testStream`: clean up state machine logic --- bzip2.rs | 426 ++++++++----------------------------------------------- 1 file changed, 59 insertions(+), 367 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index ac7b87cd4..d27eda23e 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -482,20 +482,20 @@ unsafe fn uncompressStream(zStream: *mut FILE, stream: *mut FILE) -> bool { } } -unsafe fn testStream(zStream: *mut FILE) -> Bool { - let mut current_block: u64; +unsafe fn testStream(zStream: *mut FILE) -> bool { let mut bzf: *mut libc::c_void; let mut bzerr: i32 = 0; - let mut bzerr_dummy: i32 = 0; let ret: i32; let mut i: i32; let mut obuf: [u8; 5000] = [0; 5000]; let mut unused: [u8; 5000] = [0; 5000]; let mut unusedTmpV: *mut libc::c_void = std::ptr::null_mut::(); + let mut nUnused = 0 as libc::c_int; let mut streamNo = 0 as libc::c_int; - if ferror(zStream) == 0 { - 's_29: loop { + + 'errhandler: { + loop { bzf = BZ2_bzReadOpen( &mut bzerr, zStream, @@ -505,8 +505,8 @@ unsafe fn testStream(zStream: *mut FILE) -> Bool { nUnused, ); if bzf.is_null() || bzerr != 0 as libc::c_int { - current_block = 5115833311404821621; - break; + // diverges + ioError() } streamNo += 1; while bzerr == 0 as libc::c_int { @@ -517,13 +517,11 @@ unsafe fn testStream(zStream: *mut FILE) -> Bool { 5000 as libc::c_int, ); if bzerr == -5 as libc::c_int { - current_block = 5115833311404821621; - break 's_29; + break 'errhandler; } } if bzerr != 4 as libc::c_int { - current_block = 5115833311404821621; - break; + break 'errhandler; } BZ2_bzReadGetUnused(&mut bzerr, bzf, &mut unusedTmpV, &mut nUnused); if bzerr != 0 as libc::c_int { @@ -540,369 +538,63 @@ unsafe fn testStream(zStream: *mut FILE) -> Bool { panic(b"test:bzReadGetUnused\0" as *const u8 as *const libc::c_char); } if nUnused == 0 as libc::c_int && myfeof(zStream) as libc::c_int != 0 { - current_block = 5783071609795492627; break; } } - match current_block { - 5783071609795492627 => { - if ferror(zStream) == 0 { - ret = fclose(zStream); - if ret != -1 as libc::c_int { - if verbosity >= 2 as libc::c_int { - fprintf(stderr, b"\n \0" as *const u8 as *const libc::c_char); - } - return 1 as Bool; - } - } + + if ferror(zStream) != 0 { + ioError() // diverges + } + ret = fclose(zStream); + if ret == libc::EOF { + ioError() // diverges + } + + if verbosity >= 2 { + eprintln!() + } + + return true; + } + + // errhandler: + + BZ2_bzReadClose(&mut 0, bzf); + if verbosity == 0 { + eprintln!( + "{}: {}: ", + get_program_name().display(), + CStr::from_ptr(inName.as_ptr()).to_string_lossy(), + ); + } + match bzerr { + libbzip2_rs_sys::BZ_CONFIG_ERROR => configError(), + libbzip2_rs_sys::BZ_IO_ERROR => ioError(), + libbzip2_rs_sys::BZ_DATA_ERROR => { + eprintln!("data integrity (CRC) error in data"); + false + } + libbzip2_rs_sys::BZ_MEM_ERROR => outOfMemory(), + libbzip2_rs_sys::BZ_UNEXPECTED_EOF => { + eprintln!("file ends unexpectedly"); + false + } + libbzip2_rs_sys::BZ_DATA_ERROR_MAGIC => { + if zStream != stdin { + fclose(zStream); } - _ => { - BZ2_bzReadClose(&mut bzerr_dummy, bzf); - if verbosity == 0 as libc::c_int { - fprintf( - stderr, - b"%s: %s: \0" as *const u8 as *const libc::c_char, - progName, - inName.as_mut_ptr(), - ); - } - match bzerr { - -9 => { - current_block = 7033902699813040753; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } - -6 => {} - -4 => { - current_block = 4900559648241656877; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } - -3 => { - current_block = 12127014564286193091; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } - -7 => { - current_block = 10567734636544821229; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } - -5 => { - current_block = 17188754426950485343; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } - _ => { - current_block = 14955303639559299169; - match current_block { - 14955303639559299169 => { - panic( - b"test:unexpected error\0" as *const u8 as *const libc::c_char, - ); - } - 10567734636544821229 => { - fprintf( - stderr, - b"file ends unexpectedly\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - 17188754426950485343 => { - if zStream != stdin { - fclose(zStream); - } - if streamNo == 1 as libc::c_int { - fprintf( - stderr, - b"bad magic number (file not created by bzip2)\n\0" - as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } else { - if noisy { - fprintf( - stderr, - b"trailing garbage after EOF ignored\n\0" as *const u8 - as *const libc::c_char, - ); - } - return 1 as Bool; - } - } - 7033902699813040753 => { - configError(); - } - 12127014564286193091 => { - outOfMemory(); - } - _ => { - fprintf( - stderr, - b"data integrity (CRC) error in data\n\0" as *const u8 - as *const libc::c_char, - ); - return 0 as Bool; - } - } - } + if streamNo == 1 { + eprintln!("bad magic number (file not created by bzip2)"); + false + } else { + if noisy { + eprintln!("trailing garbage after EOF ignored"); } + true } } + _ => panic_str("test:unexpected error"), } - ioError(); } unsafe fn setExit(v: i32) { @@ -2031,7 +1723,7 @@ unsafe fn testf(name: Option) { if allOK as libc::c_int != 0 && verbosity >= 1 as libc::c_int { eprintln!("ok"); } - if allOK == 0 { + if !allOK { test_fails_exists = true; } } From 4efebba59cefde24da15e2b13700135436a8734e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:33:38 +0100 Subject: [PATCH 07/10] `fn testStream`: clean up casts --- bzip2.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index d27eda23e..3a7e1e1ef 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -489,10 +489,9 @@ unsafe fn testStream(zStream: *mut FILE) -> bool { let mut i: i32; let mut obuf: [u8; 5000] = [0; 5000]; let mut unused: [u8; 5000] = [0; 5000]; - let mut unusedTmpV: *mut libc::c_void = std::ptr::null_mut::(); - let mut nUnused = 0 as libc::c_int; - let mut streamNo = 0 as libc::c_int; + let mut nUnused = 0; + let mut streamNo = 0; 'errhandler: { loop { @@ -504,40 +503,48 @@ unsafe fn testStream(zStream: *mut FILE) -> bool { unused.as_mut_ptr() as *mut libc::c_void, nUnused, ); - if bzf.is_null() || bzerr != 0 as libc::c_int { + if bzf.is_null() || bzerr != 0 { // diverges ioError() } + + // there might be multiple files if the input stream is stdin streamNo += 1; - while bzerr == 0 as libc::c_int { + + while bzerr == 0 { BZ2_bzRead( &mut bzerr, bzf, obuf.as_mut_ptr() as *mut libc::c_void, - 5000 as libc::c_int, + 5000, ); - if bzerr == -5 as libc::c_int { + if bzerr == libbzip2_rs_sys::BZ_DATA_ERROR_MAGIC { break 'errhandler; } } - if bzerr != 4 as libc::c_int { + + if bzerr != libbzip2_rs_sys::BZ_STREAM_END { break 'errhandler; } + + let mut unusedTmpV: *mut libc::c_void = std::ptr::null_mut::(); BZ2_bzReadGetUnused(&mut bzerr, bzf, &mut unusedTmpV, &mut nUnused); - if bzerr != 0 as libc::c_int { - panic(b"test:bzReadGetUnused\0" as *const u8 as *const libc::c_char); + if bzerr != libbzip2_rs_sys::BZ_OK { + panic_str("test:bzReadGetUnused"); } + let unusedTmp = unusedTmpV as *mut u8; i = 0 as libc::c_int; while i < nUnused { unused[i as usize] = *unusedTmp.offset(i as isize); i += 1; } + BZ2_bzReadClose(&mut bzerr, bzf); - if bzerr != 0 as libc::c_int { - panic(b"test:bzReadGetUnused\0" as *const u8 as *const libc::c_char); + if bzerr != libbzip2_rs_sys::BZ_OK { + panic_str("test:bzReadClose"); } - if nUnused == 0 as libc::c_int && myfeof(zStream) as libc::c_int != 0 { + if nUnused == 0 && myfeof(zStream) != 0 { break; } } From 65280ea0d2bc507a57d2051d75e0b8c05ae4b2de Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:41:47 +0100 Subject: [PATCH 08/10] `fn testf`: clean up casts --- bzip2.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index 3a7e1e1ef..e2f32a245 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -1664,7 +1664,7 @@ unsafe fn testf(name: Option) { in_name.display(), ); } - setExit(1 as libc::c_int); + setExit(1); return; } if srcMode != SourceMode::I2O && !in_name.exists() { @@ -1674,7 +1674,7 @@ unsafe fn testf(name: Option) { in_name.display(), display_last_os_error(), ); - setExit(1 as libc::c_int); + setExit(1); return; } if srcMode != SourceMode::I2O && in_name.is_dir() { @@ -1683,7 +1683,7 @@ unsafe fn testf(name: Option) { get_program_name().display(), in_name.display(), ); - setExit(1 as libc::c_int); + setExit(1); return; } match srcMode { @@ -1698,7 +1698,7 @@ unsafe fn testf(name: Option) { get_program_name().display(), get_program_name().display(), ); - setExit(1 as libc::c_int); + setExit(1); return; } inStr = stdin; @@ -1715,19 +1715,19 @@ unsafe fn testf(name: Option) { in_name.display(), display_last_os_error(), ); - setExit(1 as libc::c_int); + setExit(1); return; } } } - if verbosity >= 1 as libc::c_int { + if verbosity >= 1 { eprint!(" {}: ", in_name.display()); pad(inName.as_mut_ptr()); fflush(stderr); } outputHandleJustInCase = std::ptr::null_mut::(); let allOK = testStream(inStr); - if allOK as libc::c_int != 0 && verbosity >= 1 as libc::c_int { + if allOK && verbosity >= 1 { eprintln!("ok"); } if !allOK { From c18a11e272a659556aa2fd9f1f018796c19dae31 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 11 Nov 2024 15:45:10 +0100 Subject: [PATCH 09/10] `fn testf`: add test for no read permissions --- bzip2.rs | 2 +- tests/quick.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index e2f32a245..dd350f8dc 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -1710,7 +1710,7 @@ unsafe fn testf(name: Option) { ); if inStr.is_null() { eprintln!( - "{}: Can't open input file {}:{}.", + "{}: Can't open input {}: {}.", get_program_name().display(), in_name.display(), display_last_os_error(), diff --git a/tests/quick.rs b/tests/quick.rs index 962bdbb80..2c1046ee7 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -961,7 +961,7 @@ fn input_file_has_hard_links() { #[cfg(unix)] #[test] -fn input_file_cannot_be_read() { +fn decompress_input_file_cannot_be_read() { use std::os::unix::fs::PermissionsExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -1350,3 +1350,42 @@ fn test_input_file_is_a_directory() { ), ); } + +#[cfg(unix)] +#[test] +fn test_input_file_cannot_be_read() { + use std::os::unix::fs::PermissionsExt; + + let tmpdir = tempfile::tempdir().unwrap(); + let sample1 = tmpdir.path().join("sample1.bz2"); + + std::fs::copy("tests/input/quick/sample1.bz2", &sample1).unwrap(); + + let mut permissions = std::fs::metadata(&sample1).unwrap().permissions(); + permissions.set_mode(0o000); // no permissions for you + std::fs::set_permissions(&sample1, permissions).unwrap(); + + let mut cmd = command(); + + let output = match cmd.arg("-t").arg("-vvv").arg(&sample1).output() { + Ok(output) => output, + Err(err) => panic!("Running {cmd:?} failed with {err:?}"), + }; + + assert!( + !output.status.success(), + "status: {:?} stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + assert!(output.stdout.is_empty()); + + assert_eq!( + String::from_utf8_lossy(&output.stderr).replace(bzip2_binary(), "bzip2"), + format!( + "bzip2: Can't open input {in_file}: Permission denied.\n", + in_file = sample1.display(), + ), + ); +} From 1ca34a123d41d3c3ddbdbba876dd0a088939bf49 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 12 Nov 2024 10:02:30 +0100 Subject: [PATCH 10/10] Apply suggestions from code review Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- bzip2.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index dd350f8dc..6b69b4718 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -485,7 +485,6 @@ unsafe fn uncompressStream(zStream: *mut FILE, stream: *mut FILE) -> bool { unsafe fn testStream(zStream: *mut FILE) -> bool { let mut bzf: *mut libc::c_void; let mut bzerr: i32 = 0; - let ret: i32; let mut i: i32; let mut obuf: [u8; 5000] = [0; 5000]; let mut unused: [u8; 5000] = [0; 5000]; @@ -527,7 +526,7 @@ unsafe fn testStream(zStream: *mut FILE) -> bool { break 'errhandler; } - let mut unusedTmpV: *mut libc::c_void = std::ptr::null_mut::(); + let mut unusedTmpV = std::ptr::null_mut(); BZ2_bzReadGetUnused(&mut bzerr, bzf, &mut unusedTmpV, &mut nUnused); if bzerr != libbzip2_rs_sys::BZ_OK { panic_str("test:bzReadGetUnused"); @@ -552,8 +551,7 @@ unsafe fn testStream(zStream: *mut FILE) -> bool { if ferror(zStream) != 0 { ioError() // diverges } - ret = fclose(zStream); - if ret == libc::EOF { + if fclose(zStream) == libc::EOF { ioError() // diverges }