use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
pub const DEFAULT_PAGE_SIZE: u64 = 4096;
pub fn corrupt_database_header(path: &Path) {
let mut file =
OpenOptions::new().read(true).write(true).open(path).expect("open db file for corruption");
file.seek(SeekFrom::Start(0)).expect("seek to header");
file.write_all(b"not-a-sqlite-db!").expect("overwrite header magic");
file.flush().expect("flush header overwrite");
file.sync_all().expect("fsync after header corruption");
}
pub fn corrupt_interior_page_byte(path: &Path, page_index: u32, byte_offset: u16, xor_mask: u8) {
assert_ne!(xor_mask, 0, "corrupt_interior_page_byte: xor_mask must be non-zero");
let absolute = u64::from(page_index) * DEFAULT_PAGE_SIZE + u64::from(byte_offset);
let mut file =
OpenOptions::new().read(true).write(true).open(path).expect("open db file for corruption");
file.seek(SeekFrom::Start(absolute)).expect("seek to interior byte");
let mut buf = [0u8; 1];
file.read_exact(&mut buf).expect("read byte to corrupt");
buf[0] ^= xor_mask;
file.seek(SeekFrom::Start(absolute)).expect("seek back");
file.write_all(&buf).expect("write corrupted byte");
file.flush().expect("flush corruption");
file.sync_all().expect("fsync after interior corruption");
}
#[allow(dead_code)]
pub fn wal_sidecar_path(db_path: &Path) -> PathBuf {
let mut wal = db_path.as_os_str().to_owned();
wal.push("-wal");
PathBuf::from(wal)
}
#[allow(dead_code)]
pub fn corrupt_wal_invalid_page_size(db_path: &Path) {
let wal_path = wal_sidecar_path(db_path);
let mut file = File::create(&wal_path).expect("create -wal sidecar");
let mut header = [0u8; 32];
header[0..4].copy_from_slice(&0x377f_0683_u32.to_be_bytes());
header[4..8].copy_from_slice(&3_007_000_u32.to_be_bytes());
header[8..12].copy_from_slice(&0x0080_0000_u32.to_be_bytes());
file.write_all(&header).expect("write wal header");
file.flush().expect("flush wal header");
file.sync_all().expect("fsync after wal header corruption");
}
#[allow(dead_code)]
pub fn corrupt_embedder_profile_row(db_path: &Path) {
let conn = rusqlite::Connection::open(db_path).expect("open db for profile corruption");
conn.execute(
"UPDATE _fathomdb_embedder_profiles SET dimension = -1 WHERE profile = 'default'",
[],
)
.expect("set embedder profile dimension to -1");
conn.execute_batch("PRAGMA wal_checkpoint(TRUNCATE)").ok();
drop(conn);
}