From 3e3e0154faf5aac1ba24c86ee2fe883b4e607b1c Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Thu, 12 Mar 2026 23:15:35 +0100 Subject: [PATCH] =?UTF-8?q?fix(parsers):=20corriger=20r=C3=A9cursion=20inf?= =?UTF-8?q?inie=20CBZ=E2=86=94CBR=20causant=20un=20stack=20overflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit analyze_cbz et analyze_cbr se rappelaient mutuellement sans garde quand un fichier échouait les deux formats → stack overflow à l'analyse. Ajout d'un paramètre allow_fallback=false pour briser la boucle. Co-Authored-By: Claude Sonnet 4.6 --- crates/parsers/src/lib.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/crates/parsers/src/lib.rs b/crates/parsers/src/lib.rs index 801d584..6fc00d8 100644 --- a/crates/parsers/src/lib.rs +++ b/crates/parsers/src/lib.rs @@ -152,27 +152,30 @@ pub fn parse_metadata( /// `pdf_render_scale`: max dimension used for PDF rasterization; 0 means use default (400). pub fn analyze_book(path: &Path, format: BookFormat, pdf_render_scale: u32) -> Result<(i32, Vec)> { match format { - BookFormat::Cbz => analyze_cbz(path), - BookFormat::Cbr => analyze_cbr(path), + BookFormat::Cbz => analyze_cbz(path, true), + BookFormat::Cbr => analyze_cbr(path, true), BookFormat::Pdf => analyze_pdf(path, pdf_render_scale), } } -fn analyze_cbz(path: &Path) -> Result<(i32, Vec)> { +fn analyze_cbz(path: &Path, allow_fallback: bool) -> Result<(i32, Vec)> { let file = std::fs::File::open(path) .with_context(|| format!("cannot open cbz: {}", path.display()))?; let mut archive = match zip::ZipArchive::new(file) { Ok(a) => a, Err(e) => { - // Some .cbz files are actually RAR archives with the wrong extension — fallback to CBR parser - return analyze_cbr(path).map_err(|rar_err| { - anyhow::anyhow!( - "invalid cbz archive and RAR fallback also failed for {}: ZIP={}, RAR={}", - path.display(), - e, - rar_err - ) - }); + if allow_fallback { + // Some .cbz files are actually RAR archives with the wrong extension — fallback to CBR parser + return analyze_cbr(path, false).map_err(|rar_err| { + anyhow::anyhow!( + "invalid cbz archive and RAR fallback also failed for {}: ZIP={}, RAR={}", + path.display(), + e, + rar_err + ) + }); + } + return Err(anyhow::anyhow!("invalid cbz archive for {}: {}", path.display(), e)); } }; @@ -198,7 +201,7 @@ fn analyze_cbz(path: &Path) -> Result<(i32, Vec)> { Ok((count, buf)) } -fn analyze_cbr(path: &Path) -> Result<(i32, Vec)> { +fn analyze_cbr(path: &Path, allow_fallback: bool) -> Result<(i32, Vec)> { // Pass 1: list all image names via unrar (in-process, no subprocess) let mut image_names: Vec = { let archive = unrar::Archive::new(path) @@ -209,8 +212,8 @@ fn analyze_cbr(path: &Path) -> Result<(i32, Vec)> { Ok(a) => a, Err(e) => { let e_str = e.to_string(); - if e_str.contains("Not a RAR archive") || e_str.contains("bad archive") { - return analyze_cbz(path).map_err(|zip_err| { + if allow_fallback && (e_str.contains("Not a RAR archive") || e_str.contains("bad archive")) { + return analyze_cbz(path, false).map_err(|zip_err| { anyhow::anyhow!( "not a RAR archive and ZIP fallback also failed for {}: RAR={}, ZIP={}", path.display(), @@ -371,7 +374,7 @@ fn is_image_name(name: &str) -> bool { pub fn extract_first_page(path: &Path, format: BookFormat) -> Result> { match format { BookFormat::Cbz => extract_cbz_first_page(path), - BookFormat::Cbr => analyze_cbr(path).map(|(_, bytes)| bytes), + BookFormat::Cbr => analyze_cbr(path, true).map(|(_, bytes)| bytes), BookFormat::Pdf => analyze_pdf(path, 0).map(|(_, bytes)| bytes), } }