mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-26 03:06:56 -06:00
Integration: #783 reads the cover image with a size cap (256 MiB), devices included
This commit is contained in:
@@ -768,17 +768,23 @@ fn read_path(path: &str) -> std::result::Result<Vec<u8>, String> {
|
||||
read_path_capped(path, MAX_DATA_FILE_BYTES)
|
||||
}
|
||||
|
||||
fn read_path_capped(path: &str, cap: u64) -> std::result::Result<Vec<u8>, String> {
|
||||
match std::fs::metadata(path) {
|
||||
Ok(m) if m.len() > cap => return Err(format!("{path}: the data file is larger than {} MiB", cap / (1024 * 1024))),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Err(format!("{path}: the data file is missing")),
|
||||
_ => {}
|
||||
/// The file at `path`, refused when it is larger than `cap` bytes. The read itself stops past
|
||||
/// `cap`, so a device or a file that grows while it is read can't exhaust memory.
|
||||
pub(crate) fn read_path_capped(path: &str, cap: u64) -> std::result::Result<Vec<u8>, String> {
|
||||
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) {
|
||||
Ok(b) => Ok(b),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Err(format!("{path}: the data file is missing")),
|
||||
Err(e) => Err(format!("{path}: {e}")),
|
||||
let mut b = Vec::new();
|
||||
std::fs::File::open(path).map_err(missing)?.take(cap.saturating_add(1)).read_to_end(&mut b).map_err(missing)?;
|
||||
if b.len() as u64 > cap {
|
||||
return Err(too_big());
|
||||
}
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// coordinates on one spread (leaving a later page empty).
|
||||
#[test]
|
||||
|
||||
@@ -180,7 +180,7 @@ fn export_epub(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut warnings: Vec<String> = Vec::new();
|
||||
// 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) {
|
||||
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(),
|
||||
};
|
||||
if let Some(c) = &cover
|
||||
|
||||
Reference in New Issue
Block a user