Audit round 2 fixes: an unsafe default, four omissions, and two swallowed errors
The folder encryption probe returned "not encrypted" when it had sampled nothing at all — a title shorter than one aligned unit skipped the loop entirely. That verdict CLEARS the structural one an AACS directory raised, so a genuinely encrypted folder would have been ripped as clear and written ciphertext as video at exit 0. With no evidence it now keeps the structural verdict, and its bounds arithmetic no longer trusts disc-derived values not to wrap. Reading an IFO header swallowed every I/O error and returned an empty buffer, which sent each placement offset through unwrap_or(0) and recorded no constraint at all — a permission error on one file produced a silently misplaced VOB. The directory walk swallowed the same class while claiming to skip only vanished files. Both now propagate; only NotFound is skipped. Four things the round-1 changes left inconsistent: two new error codes had no doc comments, were absent from the io::Error mapping, printed no path in Display, and were missing from the test that proves codes are distinct. The demux sink dropped frames silently while the MKV muxer reported them. And set_clips had been inserted INTO write_frame's doc comment, leaving write_frame undocumented and its paragraphs describing the wrong function. uid/gid used 0 as "not specified"; UDF's sentinel is 0xFFFFFFFF, and 0 is root.
This commit is contained in:
+19
-10
@@ -228,7 +228,11 @@ fn walk(dir: &Path, disc_path: &str, depth: u32, entries: &mut usize) -> Result<
|
||||
Ok(m) => m,
|
||||
// A broken symlink or a file that vanished between readdir and
|
||||
// stat: skip it rather than plan an extent that cannot be read.
|
||||
Err(_) => continue,
|
||||
// Anything else — permission denied, an I/O error on the host
|
||||
// volume — is NOT a missing file, and skipping it would drop a
|
||||
// real file out of the image with nothing reported.
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
|
||||
Err(e) => return Err(Error::from(e)),
|
||||
};
|
||||
if !meta.is_file() {
|
||||
continue;
|
||||
@@ -335,16 +339,21 @@ fn be_u32(buf: &[u8], off: usize) -> Option<u32> {
|
||||
}
|
||||
|
||||
/// Read the first `n` bytes of a host file.
|
||||
fn read_head(path: &Path, n: usize) -> Vec<u8> {
|
||||
/// Read the first `n` bytes of an IFO so its placement offsets can be resolved.
|
||||
///
|
||||
/// Errors propagate. Returning an empty buffer instead would send every offset
|
||||
/// through `unwrap_or(0)`, recording NO placement constraint — and a VOB placed
|
||||
/// without its constraint yields an image that reads at the wrong offset with
|
||||
/// nothing reported. An IFO that cannot be read is a folder that cannot be
|
||||
/// planned.
|
||||
fn read_head(path: &Path, n: usize) -> Result<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
let mut buf = vec![0u8; n];
|
||||
match std::fs::File::open(path).and_then(|mut f| f.read(&mut buf)) {
|
||||
Ok(got) => {
|
||||
buf.truncate(got);
|
||||
buf
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
let got = std::fs::File::open(path)
|
||||
.and_then(|mut f| f.read(&mut buf))
|
||||
.map_err(Error::from)?;
|
||||
buf.truncate(got);
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Placement order and constraints for a `VIDEO_TS` folder.
|
||||
@@ -418,7 +427,7 @@ fn place_video_ts(vts: &mut DirNode, start: u32) -> Result<u32> {
|
||||
if let Some(c) = class
|
||||
&& c.role == Role::Ifo
|
||||
{
|
||||
let head = read_head(&vts.files[i].host, 0xC8);
|
||||
let head = read_head(&vts.files[i].host, 0xC8)?;
|
||||
let menu = be_u32(&head, 0xC0).unwrap_or(0);
|
||||
if menu != 0 {
|
||||
menu_req.insert(c.group, lba.saturating_add(menu));
|
||||
|
||||
Reference in New Issue
Block a user