#![forbid(unsafe_code)]
use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Write};
use std::process::ExitCode;
use obj_core::pager::page::PAGE_SIZE;
use obj_core::pager::{Config, Pager};
use obj_core::platform::FileHandle;
use obj_core::Catalog;
fn main() -> ExitCode {
let mut args = std::env::args_os().skip(1);
let Some(path_os) = args.next() else {
eprintln!("usage: corrupt_one_byte <path>");
return ExitCode::from(2);
};
let path = std::path::PathBuf::from(path_os);
match Pager::<FileHandle>::open(&path, Config::default()) {
Ok(p) => {
if let Err(e) = p.close() {
eprintln!("corrupt_one_byte: checkpoint failed: {e}");
return ExitCode::from(2);
}
}
Err(e) => {
eprintln!("corrupt_one_byte: open for checkpoint failed: {e}");
return ExitCode::from(2);
}
}
let target_pid = match locate_primary_root(&path) {
Ok(pid) => pid,
Err(e) => {
eprintln!("corrupt_one_byte: locate failed: {e}");
return ExitCode::from(2);
}
};
let mut file = match OpenOptions::new().read(true).write(true).open(&path) {
Ok(f) => f,
Err(e) => {
eprintln!("corrupt_one_byte: open for write failed: {e}");
return ExitCode::from(2);
}
};
let target_off = target_pid
.saturating_mul(PAGE_SIZE as u64)
.saturating_add(64);
if let Err(e) = file.seek(SeekFrom::Start(target_off)) {
eprintln!("corrupt_one_byte: seek failed: {e}");
return ExitCode::from(2);
}
if let Err(e) = file.write_all(&[0xFFu8]) {
eprintln!("corrupt_one_byte: write failed: {e}");
return ExitCode::from(2);
}
println!("corrupt_one_byte: flipped byte at offset {target_off}");
ExitCode::SUCCESS
}
fn locate_primary_root(path: &std::path::Path) -> obj::Result<u64> {
let mut pager = Pager::<FileHandle>::open(path, Config::default())?;
pager.begin_txn();
let catalog = Catalog::<FileHandle>::open_or_init(&mut pager)?;
let descriptor =
catalog
.get(&mut pager, "smoke_users")?
.ok_or(obj::Error::CollectionNotFound {
name: "smoke_users".to_owned(),
})?;
pager.end_txn();
Ok(descriptor.primary_root)
}