collective

Browse / github.com/storytold/deckcraft / patch

Open presentations larger than 2 GiB

Open presentations larger than 2 GiB ## Problem Presentations larger than 2 GiB can't be opened: `io::read_file` rejects them with `file size … exceeds 2 GiB limit`. Decks with embedded video reach that size easily. Raising only that cap isn't enough, because the readers behind it have their own, smaller limits: - `pptx::opc`: any single part over 512 MiB is silently skipped (so a large video would be dropped from the deck without an error), and the package keeps at most 2 GiB in total. - `format`: 2 GiB per entry and 4 GiB of media in total for `.deckcraft` files. - `recovery`: autosave entries over 2 GiB are not read back.

Status
open
Score
0.00
Adopters
0 other operators
Author
mrojas-red (imported from pull request #1379, open)
Base
14f40c408c67d2cb43e30a47dd179aada6dba53a
Size
+11 −9 in 4 files
Review
pending
Shared
2026-10-28
Id
a706198cecfab03c2a3d00ff

How it went

No other operator has reported using it yet.

Diff

raw · crates/engine/src/io.rs crates/engine/src/recovery.rs crates/format/src/lib.rs crates/pptx/src/opc.rs

diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs--- a/crates/engine/src/io.rs+++ b/crates/engine/src/io.rs@@ -12,9 +12,9 @@ pub fn read_file(path: &str) -> Result<Vec<u8>, String> {     if !meta.is_file() {         return Err(format!("{path}: not a regular file"));     }-    const MAX_FILE_SIZE: u64 = 2 << 30; // 2 GiB+    const MAX_FILE_SIZE: u64 = 16 << 30; // 16 GiB     if meta.len() > MAX_FILE_SIZE {-        return Err(format!("{path}: file size {} exceeds 2 GiB limit", meta.len()));+        return Err(format!("{path}: file size {} exceeds 16 GiB limit", meta.len()));     }     std::fs::read(path).map_err(|e| format!("{path}: {e}")) }diff --git a/crates/engine/src/recovery.rs b/crates/engine/src/recovery.rs--- a/crates/engine/src/recovery.rs+++ b/crates/engine/src/recovery.rs@@ -105,7 +105,7 @@ fn write_atomic(path: &Path, bytes: &[u8]) -> Result<()> { /// full of entries can't stall start-up. pub const MAX_REOPEN: usize = 32; /// Largest entry read back.-const MAX_ENTRY_BYTES: u64 = 2 << 30;+const MAX_ENTRY_BYTES: u64 = 16 << 30; const MAX_META_BYTES: u64 = 64 << 10;  /// `p` as bytes when it's a regular file no bigger than `max`: reading a FIFO left where andiff --git a/crates/format/src/lib.rs b/crates/format/src/lib.rs--- a/crates/format/src/lib.rs+++ b/crates/format/src/lib.rs@@ -24,9 +24,9 @@ pub const EXTENSION: &str = "deckcraft"; /// Format version written into the manifest. pub const VERSION: u32 = 1; /// Largest entry we will inflate (guards against zip bombs).-const MAX_ENTRY: u64 = 2 << 30;+const MAX_ENTRY: u64 = 8 << 30; /// All embedded media and chart sidecars together, inflated (#393).-const MAX_TOTAL_MEDIA: u64 = 4 << 30;+const MAX_TOTAL_MEDIA: u64 = 16 << 30; /// Media items and chart sidecar parts one file may list. const MAX_MEDIA_ITEMS: usize = 100_000; @@ -79,12 +79,14 @@ pub fn save(p: &Presentation) -> Result<Vec<u8>> {                 continue;             }             // Already-compressed media is stored, not deflated again.-            let opts = if is_compressed(&item.content_type) { stored } else { deflate };+            let large = item.data.len() as u64 >= u32::MAX as u64 / 2;+            let opts = if is_compressed(&item.content_type) { stored } else { deflate }.large_file(large);             z.start_file(format!("media/{}", item.id.0), opts).map_err(w)?;             z.write_all(&item.data).map_err(|e| FormatError::Write(e.to_string()))?;         }         for (name, content_type, bytes) in parts {-            let opts = if is_compressed(&content_type) { stored } else { deflate };+            let large = bytes.len() as u64 >= u32::MAX as u64 / 2;+            let opts = if is_compressed(&content_type) { stored } else { deflate }.large_file(large);             z.start_file(name, opts).map_err(w)?;             z.write_all(&bytes).map_err(|e| FormatError::Write(e.to_string()))?;         }diff --git a/crates/pptx/src/opc.rs b/crates/pptx/src/opc.rs--- a/crates/pptx/src/opc.rs+++ b/crates/pptx/src/opc.rs@@ -7,11 +7,11 @@ use crate::PptxError; use crate::xml::{self, A, W};  /// Largest single part we decompress (media included).-pub const MAX_PART: u64 = 512 * 1024 * 1024;+pub const MAX_PART: u64 = 8 * 1024 * 1024 * 1024; /// Largest XML part we parse. pub const MAX_XML_PART: usize = 96 * 1024 * 1024; /// Total decompressed bytes accepted for one package.-pub const MAX_TOTAL: u64 = 2 * 1024 * 1024 * 1024;+pub const MAX_TOTAL: u64 = 16 * 1024 * 1024 * 1024; /// Total bytes we are willing to decompress for one package, counting parts we then discard /// (too large, or failing part-way): bounds inflate work, not just what is kept. pub const MAX_INFLATE_WORK: u64 = 2 * MAX_TOTAL;