From 80625ba8119e230c72e679e6f090ae630eb9fe5c Mon Sep 17 00:00:00 2001 From: Adam Gashlin <adam@allspice.io> Date: Fri, 27 Sep 2024 11:52:31 -0700 Subject: [PATCH 1/2] Allow a DIFAT sector to be padded with zeroes --- src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6cfe7d7..e0009c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -435,6 +435,19 @@ impl<F: Read + Seek> CompoundFile<F> { difat_sector_ids.len() ); } + // The DIFAT should be padded with FREE_SECTOR, but DIFAT sectors + // may instead instead be incorrectly zero padded (see + // https://github.com/mdsteele/rust-cfb/issues/41). + // In case num_fat_sectors is not reliable, only remove zeroes, + // and don't remove sectors from the header DIFAT. + if !validation.is_strict() { + while difat.len() > consts::NUM_DIFAT_ENTRIES_IN_HEADER + && difat.len() > header.num_fat_sectors as usize + && difat.last() == Some(&0) + { + difat.pop(); + } + } while difat.last() == Some(&consts::FREE_SECTOR) { difat.pop(); } From cc95e84f1187cd9a5a56c12cc34bf76a840e5428 Mon Sep 17 00:00:00 2001 From: Adam Gashlin <adam@allspice.io> Date: Fri, 27 Sep 2024 11:53:59 -0700 Subject: [PATCH 2/2] Add test for zero-padded DIFAT sector --- src/lib.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e0009c3..6d342a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1075,6 +1075,102 @@ mod tests { CompoundFile::open(Cursor::new(data)).expect("open"); } + fn make_cfb_file_with_zero_padded_difat() -> io::Result<Vec<u8>> { + let version = Version::V3; + let mut data = Vec::<u8>::new(); + + let dir_sector = 0; + let difat_sector = 1; + // The zero-padded DIFAT issue is only seen with a DIFAT sector + let num_fat_sectors = consts::NUM_DIFAT_ENTRIES_IN_HEADER + 1; + // Layout FAT sectors after the DIFAT sector + let first_fat_sector = difat_sector + 1; + let fat_sectors: Vec<u32> = (0..num_fat_sectors) + .map(|i| (first_fat_sector + i) as u32) + .collect(); + + // Construct header full of DIFAT entries + let header = Header { + version, + num_dir_sectors: 0, + num_fat_sectors: num_fat_sectors as u32, + first_dir_sector: dir_sector as u32, + first_minifat_sector: consts::END_OF_CHAIN, + num_minifat_sectors: 0, + first_difat_sector: difat_sector as u32, + num_difat_sectors: 1, + initial_difat_entries: std::array::from_fn(|difat_entry_i| { + fat_sectors[difat_entry_i] + }), + }; + header.write_to(&mut data)?; + + // Write the directory sector + DirEntry::empty_root_entry().write_to(&mut data)?; + for _ in 1..version.dir_entries_per_sector() { + DirEntry::unallocated().write_to(&mut data)?; + } + + // Write the DIFAT sector + let num_difat_entries_in_sector = + version.sector_len() / size_of::<u32>() - 1; + for i in 0..num_difat_entries_in_sector { + let difat_entry_i = i + consts::NUM_DIFAT_ENTRIES_IN_HEADER; + + let entry = if difat_entry_i < num_fat_sectors { + fat_sectors[difat_entry_i] + } else { + // Pad with zeroes instead of FREE_SECTOR, this is + // the point where it deviates from spec. + 0 + }; + data.write_u32::<LittleEndian>(entry)?; + } + // End DIFAT chain + data.write_u32::<LittleEndian>(consts::END_OF_CHAIN)?; + + // Write the first two FAT sectors, referencing the header data + let num_fat_entries_in_sector = + version.sector_len() / size_of::<u32>(); + let mut fat = vec![consts::FREE_SECTOR; num_fat_entries_in_sector * 2]; + fat[difat_sector] = consts::DIFAT_SECTOR; + fat[dir_sector] = consts::END_OF_CHAIN; + for fat_sector in fat_sectors { + fat[fat_sector as usize] = consts::FAT_SECTOR; + } + for entry in fat { + data.write_u32::<LittleEndian>(entry)?; + } + + // Pad out the rest of the FAT sectors with FREE_SECTOR + for _fat_sector in 2..num_fat_sectors { + for _i in 0..num_fat_entries_in_sector { + data.write_u32::<LittleEndian>(consts::FREE_SECTOR)?; + } + } + + Ok(data) + } + + #[test] + fn zero_padded_difat_strict() { + let data = make_cfb_file_with_zero_padded_difat().unwrap(); + let result = CompoundFile::open_strict(Cursor::new(data)); + assert_eq!( + result.err().unwrap().to_string(), + "Incorrect number of FAT sectors (header says 110, DIFAT says 236)", + ); + } + + // Regression test for https://github.com/mdsteele/rust-cfb/issues/41. + #[test] + fn zero_padded_difat_permissive() { + let data = make_cfb_file_with_zero_padded_difat().unwrap(); + // Despite the zero-padded DIFAT, we should be able to read this file + // under Permissive validation. + CompoundFile::open(Cursor::new(data)).expect("open"); + } + // Regression test for https://github.com/mdsteele/rust-cfb/issues/52. #[test] fn update_num_dir_sectors() {