mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-05 10:10:35 +00:00
Integration: #783 reads the cover image with a size cap (256 MiB), devices included
This commit is contained in:
1 parent
73112e882f
commit
6eae3620ed
3 files changed
+25
-10
No files matched your search
@@ -768,17 +768,23 @@ fn read_path(path: &str) -> std::result::Result<Vec<u8>, String> {
|
|||||||
read_path_capped(path, MAX_DATA_FILE_BYTES)
|
read_path_capped(path, MAX_DATA_FILE_BYTES)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_path_capped(path: &str, cap: u64) -> std::result::Result<Vec<u8>, String> {
|
/// The file at `path`, refused when it is larger than `cap` bytes. The read itself stops past
|
||||||
match std::fs::metadata(path) {
|
/// `cap`, so a device or a file that grows while it is read can't exhaust memory.
|
||||||
Ok(m) if m.len() > cap => return Err(format!("{path}: the data file is larger than {} MiB", cap / (1024 * 1024))),
|
pub(crate) fn read_path_capped(path: &str, cap: u64) -> std::result::Result<Vec<u8>, String> {
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Err(format!("{path}: the data file is missing")),
|
use std::io::Read;
|
||||||
_ => {}
|
let too_big = || format!("{path}: the file is larger than {} MiB", cap / (1024 * 1024));
|
||||||
|
let missing = |e: std::io::Error| {
|
||||||
|
if e.kind() == std::io::ErrorKind::NotFound { format!("{path}: the file is missing") } else { format!("{path}: {e}") }
|
||||||
|
};
|
||||||
|
if std::fs::metadata(path).map_err(missing)?.len() > cap {
|
||||||
|
return Err(too_big());
|
||||||
}
|
}
|
||||||
match std::fs::read(path) {
|
let mut b = Vec::new();
|
||||||
Ok(b) => Ok(b),
|
std::fs::File::open(path).map_err(missing)?.take(cap.saturating_add(1)).read_to_end(&mut b).map_err(missing)?;
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Err(format!("{path}: the data file is missing")),
|
if b.len() as u64 > cap {
|
||||||
Err(e) => Err(format!("{path}: {e}")),
|
return Err(too_big());
|
||||||
}
|
}
|
||||||
|
Ok(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_dir(path: &str) -> Option<PathBuf> {
|
fn parent_dir(path: &str) -> Option<PathBuf> {
|
||||||
|
|||||||
@@ -1086,6 +1086,15 @@ fn oversized_data_file_is_refused_before_reading() {
|
|||||||
assert_eq!(read_path_capped(&path, 4096).unwrap().len(), 2048);
|
assert_eq!(read_path_capped(&path, 4096).unwrap().len(), 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A device reports no length; the read still stops at the cap (an EPUB `coverImage` of
|
||||||
|
/// `/dev/zero` must not read until memory runs out).
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn endless_file_is_refused_at_the_cap() {
|
||||||
|
let msg = read_path_capped("/dev/zero", 1024).unwrap_err();
|
||||||
|
assert!(msg.contains("larger than"), "{msg}");
|
||||||
|
}
|
||||||
|
|
||||||
/// #198: with facing pages on, one record per page must not stack the copies at identical
|
/// #198: with facing pages on, one record per page must not stack the copies at identical
|
||||||
/// coordinates on one spread (leaving a later page empty).
|
/// coordinates on one spread (leaving a later page empty).
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ fn export_epub(s: &mut Session, p: &Value) -> Result<Value> {
|
|||||||
let mut warnings: Vec<String> = Vec::new();
|
let mut warnings: Vec<String> = Vec::new();
|
||||||
// A cover image file (PNG or JPEG), else the first page rendered when `cover` asks for it.
|
// A cover image file (PNG or JPEG), else the first page rendered when `cover` asks for it.
|
||||||
let cover = match p.get("coverImage").and_then(Value::as_str).filter(|_| !fixed) {
|
let cover = match p.get("coverImage").and_then(Value::as_str).filter(|_| !fixed) {
|
||||||
Some(path) => Some(std::fs::read(path).map_err(|e| crate::EngineError::Other(format!("{path}: {e}")))?),
|
Some(path) => Some(super::datamerge::read_path_capped(path, super::datamerge::MAX_IMAGE_FILE_BYTES).map_err(crate::EngineError::Other)?),
|
||||||
None => (want_cover && !fixed).then(|| rr.render_page(&st.doc, &s.cache, 0, 2.0, false, &ropts).map(|img| img.to_png())).flatten(),
|
None => (want_cover && !fixed).then(|| rr.render_page(&st.doc, &s.cache, 0, 2.0, false, &ropts).map(|img| img.to_png())).flatten(),
|
||||||
};
|
};
|
||||||
if let Some(c) = &cover
|
if let Some(c) = &cover
|
||||||
|
|||||||
Reference in new issue
Block a user