fix(xtask): render-oracle writes page pixels into a private temporary folder (#5988)

This commit is contained in:
Dylan M. Taylor
2026-11-09 18:38:01 +08:00
committed by GitHub
parent 4cdc3d15dd
commit f800c12a7e
+51 -3
View File
@@ -323,6 +323,35 @@ pub fn text_oracle(args: &[String]) -> anyhow::Result<()> {
Ok(())
}
/// A new folder in the temporary directory that only this user can open (0700 on Unix), removed
/// when dropped, on success and on error alike.
struct PrivateDir(PathBuf);
impl PrivateDir {
fn new(prefix: &str) -> anyhow::Result<Self> {
let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |d| d.subsec_nanos());
for attempt in 0..100u32 {
let path = std::env::temp_dir().join(format!("{prefix}-{}-{nanos}-{attempt}", std::process::id()));
let mut builder = std::fs::DirBuilder::new();
#[cfg(unix)]
std::os::unix::fs::DirBuilderExt::mode(&mut builder, 0o700);
// `create`, not `create_all`: a name that is already there is never used.
match builder.create(&path) {
Ok(()) => return Ok(Self(path)),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
Err(e) => return Err(e).with_context(|| format!("create {}", path.display())),
}
}
bail!("no unused temporary folder name for {prefix}")
}
}
impl Drop for PrivateDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
/// Compare our rendering against Poppler's, page 1 of every PDF in a directory.
///
/// This is the measured half of the fidelity gap (`docs/gaps.md` gap 12): until it exists, every
@@ -368,7 +397,10 @@ pub fn render_oracle(args: &[String]) -> anyhow::Result<()> {
.collect();
files.sort();
let out_pam = std::env::temp_dir().join(format!("pdfcraft-render-oracle-{}.pam", std::process::id()));
// The page's pixels go into a folder only this user can open, made for this run (never an
// existing one, which another user could have put a link in), and removed with it.
let private = PrivateDir::new("pdfcraft-render-oracle")?;
let out_pam = private.0.join("page.pam");
let (mut scores, mut sizes_differed, mut ours_failed, mut theirs_failed) = (Vec::new(), 0usize, 0usize, 0usize);
let mut worst: Vec<(f64, String)> = Vec::new();
for file in files.iter().take(limit) {
@@ -410,7 +442,7 @@ pub fn render_oracle(args: &[String]) -> anyhow::Result<()> {
Err(_) => sizes_differed = sizes_differed.saturating_add(1),
}
}
let _ = std::fs::remove_file(&out_pam);
drop(private);
if scores.is_empty() {
bail!(
@@ -551,7 +583,23 @@ fn f1(a: &BTreeMap<String, usize>, b: &BTreeMap<String, usize>) -> f64 {
#[cfg(test)]
mod tests {
use super::{f1, order_score, root, word_sequence, words};
use super::{PrivateDir, f1, order_score, root, word_sequence, words};
/// Each run gets a new folder of its own, private to the user, gone when the run ends.
#[test]
fn the_oracle_folder_is_new_private_and_removed() {
let (a, b) = (PrivateDir::new("pdfcraft-private-test").unwrap(), PrivateDir::new("pdfcraft-private-test").unwrap());
assert_ne!(a.0, b.0);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
assert_eq!(std::fs::metadata(&a.0).unwrap().permissions().mode() & 0o777, 0o700);
}
let path = a.0.clone();
std::fs::write(path.join("page.pam"), b"x").unwrap();
drop(a);
assert!(!path.exists());
}
/// The two scores answer different questions, and the point of adding the second one is the
/// first row: F1 cannot tell a page from the same page read backwards.