use std::{
fs::OpenOptions,
io::{Read, Seek, SeekFrom, Write},
process::ExitCode,
};
use data_encoding::HEXLOWER;
use syd::{
err::SydResult,
path::{XPath, XPathBuf},
rng::fillrandom,
};
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
syd::main! {
use lexopt::prelude::*;
syd::set_sigpipe_dfl()?;
let mut opt_index = None;
let mut opt_random = false;
let mut opt_filename = None;
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') => {
help();
return Ok(ExitCode::SUCCESS);
}
Short('i') => opt_index = Some(parser.value()?.parse::<usize>()?),
Short('r') => opt_random = true,
Value(filename) if opt_filename.is_none() => {
opt_filename = Some(XPathBuf::from(filename))
}
_ => return Err(arg.unexpected().into()),
}
}
if let Some(filename) = opt_filename {
match (opt_index, opt_random) {
(Some(idx), false) => flip_bit_in_file(&filename, idx)?,
(None, true) => flip_random_bit_in_file(&filename)?,
_ => {
eprintln!("syd-bit: Exactly one of -i <index> or -r must be given!");
help();
return Ok(ExitCode::FAILURE);
}
}
} else {
eprintln!("syd-bit: File name not specified!");
help();
return Ok(ExitCode::FAILURE);
}
Ok(ExitCode::SUCCESS)
}
fn flip_bit_in_file(file_name: &XPath, bit_index: usize) -> SydResult<()> {
eprintln!("syd-bit: opening file {file_name}...");
#[expect(clippy::disallowed_methods)]
let mut file = OpenOptions::new().read(true).write(true).open(file_name)?;
let byte_index = bit_index / 8;
let bit_in_byte = bit_index % 8;
eprintln!("syd-bit: seeking to byte index {byte_index}...");
file.seek(SeekFrom::Start(byte_index as u64))?;
let mut byte = [0u8; 1];
file.read_exact(&mut byte)?;
eprintln!(
"syd-bit: read byte 0x{} at index {byte_index}.",
HEXLOWER.encode(&byte)
);
byte[0] ^= 1 << bit_in_byte;
eprintln!(
"syd-bit: flipped bit {bit_in_byte} resulting in 0x{}.",
HEXLOWER.encode(&byte)
);
eprintln!("syd-bit: moving back to file offset {byte_index}.");
file.seek(SeekFrom::Start(byte_index as u64))?;
eprintln!(
"syd-bit: writing byte 0x{} at index {byte_index}...",
HEXLOWER.encode(&byte)
);
file.write_all(&byte)?;
eprintln!("syd-bit: flipped bit:{bit_index} of byte:{byte_index}.");
Ok(())
}
fn flip_random_bit_in_file(file_name: &XPath) -> SydResult<()> {
#[expect(clippy::disallowed_methods)]
let mut file = OpenOptions::new().read(true).write(true).open(file_name)?;
let file_len = file.seek(SeekFrom::End(0))?; let total_bits = file_len * 8;
let mut rng_buf = [0u8; size_of::<usize>()];
fillrandom(&mut rng_buf)?; let bit_index = usize::from_ne_bytes(rng_buf) % total_bits as usize;
flip_bit_in_file(file_name, bit_index)
}
fn help() {
println!("Usage: syd-bit [-h] -i <idx> <file> | -r <file>");
println!("Utility to flip bits in files");
println!(" -i <idx> Flip the bit at index <idx> in the file");
println!(" -r Flip a random bit in the file");
}