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
@@ -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}")) }@@ -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 an@@ -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()))?; }@@ -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;