diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs index 5ffc345e53..583da36e89 100644 --- a/crates/engine/src/io.rs +++ b/crates/engine/src/io.rs @@ -771,8 +771,8 @@ pub fn list_dir(path: &std::path::Path, max: usize) -> Result<(Vec, bo #[cfg(not(target_arch = "wasm32"))] pub fn package_title(path: &std::path::Path, max_file: u64, max_part: u64) -> Option { crate::file_access::check_read(&path.to_string_lossy()).ok()?; - let file = std::fs::File::open(path).ok()?; - if file.metadata().ok()?.len() > max_file { + let (file, meta) = open_regular_file(path).ok()?; + if meta.len() > max_file { return None; } wordcraft_docx::package_title(std::io::BufReader::new(file), max_part) diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 4e5c490c8e..2718ef8de6 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -5747,6 +5747,39 @@ fn opening_a_named_pipe_or_device_is_refused_without_blocking() { assert!(device.unwrap_err().contains("not a file")); } +#[cfg(unix)] +#[test] +fn reading_a_template_title_from_a_named_pipe_is_refused_without_blocking() { + let dir = scratch_dir("template-title-fifo"); + let fifo = dir.join("pipe.dotx"); + + let made = std::process::Command::new("mkfifo").arg(&fifo).status(); + if !made.is_ok_and(|s| s.success()) { + let _ = std::fs::remove_dir_all(&dir); + eprintln!("mkfifo unavailable; skipping"); + return; + } + + let (tx, rx) = std::sync::mpsc::channel(); + let path = fifo.clone(); + + std::thread::spawn(move || { + let title = crate::io::package_title(&path, 64 << 20, 64 << 10); + let _ = tx.send(title); + }); + + let result = rx.recv_timeout(std::time::Duration::from_secs(10)); + + if result.is_err() { + // Unblock a stuck reader so the test thread can finish. + let _ = std::os::unix::fs::OpenOptionsExt::custom_flags(std::fs::OpenOptions::new().write(true), libc::O_NONBLOCK).open(&fifo); + } + + let _ = std::fs::remove_dir_all(&dir); + + assert_eq!(result.expect("reading a template title blocked on a named pipe"), None); +} + #[cfg(unix)] #[test] fn html_picture_that_is_a_named_pipe_is_skipped_without_blocking() {