refactor: name the output/input paths passed to OutSink
OutSink::open_with_options took two adjacent positional Option<&Path> parameters (output_file, input_file). The type system cannot tell them apart, so a future call site that swaps them still compiles - and the same-file-aliasing guard would then check the wrong path, silently re-enabling the accidental-overwrite protection bypass it exists to prevent. All three current call sites were correct; this hardens the signature before a wrong one appears. Introduce OutSinkPaths, a struct with named output_file/input_file fields, so every call site must label which path plays which role. No behavior change. Test plan: cargo clippy (default/--tests) clean; cargo test passes all 56 tests.
This commit is contained in:
+12
-6
@@ -179,8 +179,10 @@ pub fn encrypt(
|
||||
let plaintext_length = input.length;
|
||||
let mut f_plain = AheadReader::from(input.reader, chunk_sz);
|
||||
let mut f_encrypted = OutSink::open_with_options(
|
||||
options.output_file.as_deref(),
|
||||
options.input_file.as_deref(),
|
||||
OutSinkPaths {
|
||||
output_file: options.output_file.as_deref(),
|
||||
input_file: options.input_file.as_deref(),
|
||||
},
|
||||
&options.output,
|
||||
)?;
|
||||
|
||||
@@ -292,8 +294,10 @@ pub fn decrypt(
|
||||
|
||||
let mut f_encrypted = AheadReader::from(reader, cipher_chunk);
|
||||
let mut f_plain = OutSink::open_with_options(
|
||||
options.output_file.as_deref(),
|
||||
options.input_file.as_deref(),
|
||||
OutSinkPaths {
|
||||
output_file: options.output_file.as_deref(),
|
||||
input_file: options.input_file.as_deref(),
|
||||
},
|
||||
&options.output,
|
||||
)?;
|
||||
|
||||
@@ -423,8 +427,10 @@ pub fn decrypt_range(
|
||||
let last_idx = n_chunks - 1;
|
||||
|
||||
let mut out = OutSink::open_with_options(
|
||||
options.output_file.as_deref(),
|
||||
Some(&options.input_file),
|
||||
OutSinkPaths {
|
||||
output_file: options.output_file.as_deref(),
|
||||
input_file: Some(&options.input_file),
|
||||
},
|
||||
&options.output,
|
||||
)?;
|
||||
|
||||
|
||||
+13
-4
@@ -198,6 +198,16 @@ fn output_aliases_input(output: &Path, input: Option<&Path>) -> io::Result<bool>
|
||||
}
|
||||
}
|
||||
|
||||
/// Paths for [`OutSink::open_with_options`], named so the output and input
|
||||
/// roles cannot be swapped silently at a call site (both are `Option<&Path>`).
|
||||
pub(crate) struct OutSinkPaths<'a> {
|
||||
/// Where the finished output is renamed to; `None` means stdout.
|
||||
pub output_file: Option<&'a Path>,
|
||||
/// The input being read; only consulted by the aliasing guard that
|
||||
/// permits in-place overwrite of the input without `--force`.
|
||||
pub input_file: Option<&'a Path>,
|
||||
}
|
||||
|
||||
/// Output sink that supports atomic file replacement.
|
||||
///
|
||||
/// For file outputs: bytes are written to a private, randomly named temp file.
|
||||
@@ -219,11 +229,10 @@ pub(crate) enum OutSink {
|
||||
|
||||
impl OutSink {
|
||||
pub(crate) fn open_with_options(
|
||||
output_file: Option<&Path>,
|
||||
input_file: Option<&Path>,
|
||||
paths: OutSinkPaths<'_>,
|
||||
options: &OutputOptions,
|
||||
) -> io::Result<Self> {
|
||||
match output_file {
|
||||
match paths.output_file {
|
||||
None if options.buffer_verify_stdout => {
|
||||
let dir = temp_dir_for_stdout(options.temp_dir.as_deref());
|
||||
Ok(Self::BufferVerify {
|
||||
@@ -235,7 +244,7 @@ impl OutSink {
|
||||
let final_path = f.to_path_buf();
|
||||
if final_path.exists()
|
||||
&& !options.force
|
||||
&& !output_aliases_input(&final_path, input_file)?
|
||||
&& !output_aliases_input(&final_path, paths.input_file)?
|
||||
{
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
|
||||
Reference in New Issue
Block a user