diff --git a/src/internal/directory.rs b/src/internal/directory.rs index 79535a0..7020427 100644 --- a/src/internal/directory.rs +++ b/src/internal/directory.rs @@ -57,12 +57,12 @@ impl Directory { pub fn stream_id_for_name_chain(&self, names: &[&str]) -> Option { let mut stream_id = consts::ROOT_STREAM_ID; for name in names.iter() { - stream_id = self.dir_entry(stream_id).child; + stream_id = self.try_dir_entry(stream_id)?.child; loop { if stream_id == consts::NO_STREAM { return None; } - let dir_entry = self.dir_entry(stream_id); + let dir_entry = self.try_dir_entry(stream_id)?; match internal::path::compare_names(name, &dir_entry.name) { Ordering::Equal => break, Ordering::Less => stream_id = dir_entry.left_sibling, @@ -85,6 +85,10 @@ impl Directory { self.dir_entry(consts::ROOT_STREAM_ID) } + pub fn try_dir_entry(&self, stream_id: u32) -> Option<&DirEntry> { + self.dir_entries.get(stream_id as usize) + } + pub fn dir_entry(&self, stream_id: u32) -> &DirEntry { &self.dir_entries[stream_id as usize] } @@ -138,58 +142,80 @@ impl Directory { if parent_is_red && node_is_red && validation.is_strict() { malformed!("RB tree has adjacent red nodes"); } - let left_sibling = dir_entry.left_sibling; + let mut left_sibling = dir_entry.left_sibling; if left_sibling != consts::NO_STREAM { if left_sibling as usize >= self.dir_entries.len() { - malformed!( - "left sibling index is {}, but directory entry count \ - is {}", - left_sibling, - self.dir_entries.len() - ); + if validation.is_strict() { + malformed!( + "left sibling index is {}, but directory entry count \ + is {}", + left_sibling, + self.dir_entries.len() + ); + } else { + left_sibling = consts::NO_STREAM; + } } - let entry = &self.dir_entry(left_sibling); - if internal::path::compare_names(&entry.name, &dir_entry.name) - != Ordering::Less - { - malformed!( - "name ordering, {:?} vs {:?}", - dir_entry.name, - entry.name - ); + if left_sibling != consts::NO_STREAM { + let entry = &self.dir_entry(left_sibling); + if internal::path::compare_names( + &entry.name, + &dir_entry.name, + ) != Ordering::Less + { + malformed!( + "name ordering, {:?} vs {:?}", + dir_entry.name, + entry.name + ); + } + stack.push((left_sibling, node_is_red)); } - stack.push((left_sibling, node_is_red)); } - let right_sibling = dir_entry.right_sibling; + let mut right_sibling = dir_entry.right_sibling; if right_sibling != consts::NO_STREAM { if right_sibling as usize >= self.dir_entries.len() { - malformed!( - "right sibling index is {}, but directory entry count \ - is {}", - right_sibling, self.dir_entries.len()); + if validation.is_strict() { + malformed!( + "right sibling index is {}, but directory entry count \ + is {}", + right_sibling, self.dir_entries.len()); + } else { + right_sibling = consts::NO_STREAM; + } } - let entry = &self.dir_entry(right_sibling); - if internal::path::compare_names(&dir_entry.name, &entry.name) - != Ordering::Less - { - malformed!( - "name ordering, {:?} vs {:?}", - dir_entry.name, - entry.name - ); + if right_sibling != consts::NO_STREAM { + let entry = &self.dir_entry(right_sibling); + if internal::path::compare_names( + &dir_entry.name, + &entry.name, + ) != Ordering::Less + { + malformed!( + "name ordering, {:?} vs {:?}", + dir_entry.name, + entry.name + ); + } + stack.push((right_sibling, node_is_red)); } - stack.push((right_sibling, node_is_red)); } - let child = dir_entry.child; + let mut child = dir_entry.child; if child != consts::NO_STREAM { if child as usize >= self.dir_entries.len() { - malformed!( - "child index is {}, but directory entry count is {}", - child, - self.dir_entries.len() - ); + if validation.is_strict() { + malformed!( + "child index is {}, but directory entry count is {}", + child, + self.dir_entries.len() + ); + } else { + child = consts::NO_STREAM; + } + } + if child != consts::NO_STREAM { + stack.push((child, false)); } - stack.push((child, false)); } } Ok(()) diff --git a/src/internal/entry.rs b/src/internal/entry.rs index 14637cf..a304662 100644 --- a/src/internal/entry.rs +++ b/src/internal/entry.rs @@ -150,8 +150,12 @@ impl<'a, F> Entries<'a, F> { fn stack_left_spine(&mut self, parent_path: &Path, mut current_id: u32) { let minialloc = self.minialloc.read().unwrap(); while current_id != consts::NO_STREAM { - self.stack.push((parent_path.to_path_buf(), current_id, true)); - current_id = minialloc.dir_entry(current_id).left_sibling; + if let Some(dir_entry) = minialloc.try_dir_entry(current_id) { + self.stack.push((parent_path.to_path_buf(), current_id, true)); + current_id = dir_entry.left_sibling; + } else { + break; + } } } } diff --git a/src/internal/minialloc.rs b/src/internal/minialloc.rs index c5d1d8e..d663c52 100644 --- a/src/internal/minialloc.rs +++ b/src/internal/minialloc.rs @@ -96,6 +96,10 @@ impl MiniAllocator { self.directory.root_dir_entry() } + pub fn try_dir_entry(&self, stream_id: u32) -> Option<&DirEntry> { + self.directory.try_dir_entry(stream_id) + } + pub fn dir_entry(&self, stream_id: u32) -> &DirEntry { self.directory.dir_entry(stream_id) } diff --git a/src/lib.rs b/src/lib.rs index 6cfe7d7..0934d20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -511,12 +511,16 @@ impl CompoundFile { current_dir_sector ); } else if current_dir_sector >= num_sectors { - invalid_data!( - "Directory chain includes sector index {}, but sector \ - count is only {}", - current_dir_sector, - num_sectors - ); + if validation.is_strict() { + invalid_data!( + "Directory chain includes sector index {}, but sector \ + count is only {}", + current_dir_sector, + num_sectors + ); + } else { + break; + } } if seen_dir_sectors.contains(¤t_dir_sector) { invalid_data!(