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 plaintext_length = input.length;
|
||||||
let mut f_plain = AheadReader::from(input.reader, chunk_sz);
|
let mut f_plain = AheadReader::from(input.reader, chunk_sz);
|
||||||
let mut f_encrypted = OutSink::open_with_options(
|
let mut f_encrypted = OutSink::open_with_options(
|
||||||
options.output_file.as_deref(),
|
OutSinkPaths {
|
||||||
options.input_file.as_deref(),
|
output_file: options.output_file.as_deref(),
|
||||||
|
input_file: options.input_file.as_deref(),
|
||||||
|
},
|
||||||
&options.output,
|
&options.output,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -292,8 +294,10 @@ pub fn decrypt(
|
|||||||
|
|
||||||
let mut f_encrypted = AheadReader::from(reader, cipher_chunk);
|
let mut f_encrypted = AheadReader::from(reader, cipher_chunk);
|
||||||
let mut f_plain = OutSink::open_with_options(
|
let mut f_plain = OutSink::open_with_options(
|
||||||
options.output_file.as_deref(),
|
OutSinkPaths {
|
||||||
options.input_file.as_deref(),
|
output_file: options.output_file.as_deref(),
|
||||||
|
input_file: options.input_file.as_deref(),
|
||||||
|
},
|
||||||
&options.output,
|
&options.output,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -423,8 +427,10 @@ pub fn decrypt_range(
|
|||||||
let last_idx = n_chunks - 1;
|
let last_idx = n_chunks - 1;
|
||||||
|
|
||||||
let mut out = OutSink::open_with_options(
|
let mut out = OutSink::open_with_options(
|
||||||
options.output_file.as_deref(),
|
OutSinkPaths {
|
||||||
Some(&options.input_file),
|
output_file: options.output_file.as_deref(),
|
||||||
|
input_file: Some(&options.input_file),
|
||||||
|
},
|
||||||
&options.output,
|
&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.
|
/// Output sink that supports atomic file replacement.
|
||||||
///
|
///
|
||||||
/// For file outputs: bytes are written to a private, randomly named temp file.
|
/// For file outputs: bytes are written to a private, randomly named temp file.
|
||||||
@@ -219,11 +229,10 @@ pub(crate) enum OutSink {
|
|||||||
|
|
||||||
impl OutSink {
|
impl OutSink {
|
||||||
pub(crate) fn open_with_options(
|
pub(crate) fn open_with_options(
|
||||||
output_file: Option<&Path>,
|
paths: OutSinkPaths<'_>,
|
||||||
input_file: Option<&Path>,
|
|
||||||
options: &OutputOptions,
|
options: &OutputOptions,
|
||||||
) -> io::Result<Self> {
|
) -> io::Result<Self> {
|
||||||
match output_file {
|
match paths.output_file {
|
||||||
None if options.buffer_verify_stdout => {
|
None if options.buffer_verify_stdout => {
|
||||||
let dir = temp_dir_for_stdout(options.temp_dir.as_deref());
|
let dir = temp_dir_for_stdout(options.temp_dir.as_deref());
|
||||||
Ok(Self::BufferVerify {
|
Ok(Self::BufferVerify {
|
||||||
@@ -235,7 +244,7 @@ impl OutSink {
|
|||||||
let final_path = f.to_path_buf();
|
let final_path = f.to_path_buf();
|
||||||
if final_path.exists()
|
if final_path.exists()
|
||||||
&& !options.force
|
&& !options.force
|
||||||
&& !output_aliases_input(&final_path, input_file)?
|
&& !output_aliases_input(&final_path, paths.input_file)?
|
||||||
{
|
{
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::AlreadyExists,
|
io::ErrorKind::AlreadyExists,
|
||||||
|
|||||||
Reference in New Issue
Block a user