1#![warn(missing_docs)]
47
48use std::fmt;
49use std::fs;
50use std::io::{self, Read, Seek, SeekFrom, Write};
51use std::mem::size_of;
52use std::path::{Path, PathBuf};
53use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
54
55use fnv::FnvHashSet;
56use uuid::Uuid;
57
58use crate::internal::consts;
59use crate::internal::DEFAULT_STREAM_MAX_BUFFER_SIZE;
60use crate::internal::{
61 Allocator, DirEntry, Directory, EntriesOrder, Header, MiniAllocator,
62 ObjType, SectorInit, Sectors, Timestamp, Validation,
63};
64pub use crate::internal::{Entries, Entry, Stream, Version};
65
66#[macro_use]
67mod internal;
68
69pub fn open<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
73 OpenOptions::new().open(path)
74}
75
76pub fn open_rw<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
78 OpenOptions::new().open_rw(path)
79}
80
81pub fn create<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
86 OpenOptions::new().create(path)
87}
88
89pub struct OpenOptions {
93 pub(crate) max_buffer_size: usize,
94 pub(crate) validation: Validation,
95}
96
97impl OpenOptions {
98 pub fn new() -> Self {
100 OpenOptions::default()
101 }
102
103 pub fn max_buffer_size(mut self, size: usize) -> Self {
109 self.max_buffer_size = size;
110 self
111 }
112
113 pub fn strict(mut self) -> Self {
115 self.validation = Validation::Strict;
116 self
117 }
118
119 pub fn open<P: AsRef<Path>>(
121 self,
122 path: P,
123 ) -> io::Result<CompoundFile<fs::File>> {
124 self.open_with(fs::File::open(path)?)
125 }
126
127 pub fn open_rw<P: AsRef<Path>>(
129 self,
130 path: P,
131 ) -> io::Result<CompoundFile<fs::File>> {
132 let file = fs::OpenOptions::new().read(true).write(true).open(path)?;
133 self.open_with(file)
134 }
135
136 pub fn create<P: AsRef<Path>>(
141 self,
142 path: P,
143 ) -> io::Result<CompoundFile<fs::File>> {
144 let file = fs::OpenOptions::new()
145 .read(true)
146 .write(true)
147 .create(true)
148 .truncate(true)
149 .open(path)?;
150 self.create_with(file)
151 }
152
153 pub fn open_with<F: Read + Seek>(
155 self,
156 inner: F,
157 ) -> io::Result<CompoundFile<F>> {
158 CompoundFile::open_internal(
159 inner,
160 self.validation,
161 self.max_buffer_size,
162 )
163 }
164
165 pub fn create_with<F: Read + Write + Seek>(
167 self,
168 inner: F,
169 ) -> io::Result<CompoundFile<F>> {
170 CompoundFile::create_with_version_and_options(
171 Version::V4,
172 inner,
173 self.max_buffer_size,
174 )
175 }
176}
177
178impl Default for OpenOptions {
179 fn default() -> Self {
180 OpenOptions {
181 max_buffer_size: DEFAULT_STREAM_MAX_BUFFER_SIZE,
182 validation: Validation::Permissive,
183 }
184 }
185}
186
187pub struct CompoundFile<F> {
193 minialloc: Arc<RwLock<MiniAllocator<F>>>,
194 max_buffer_size: usize,
195}
196
197impl<F> CompoundFile<F> {
198 fn minialloc(&self) -> RwLockReadGuard<'_, MiniAllocator<F>> {
199 self.minialloc.read().unwrap()
200 }
201
202 fn minialloc_mut(&mut self) -> RwLockWriteGuard<'_, MiniAllocator<F>> {
203 self.minialloc.write().unwrap()
204 }
205
206 pub fn version(&self) -> Version {
208 self.minialloc().version()
209 }
210
211 fn stream_id_for_name_chain(&self, names: &[&str]) -> Option<u32> {
212 self.minialloc().stream_id_for_name_chain(names)
213 }
214
215 pub fn root_entry(&self) -> Entry {
218 Entry::new(self.minialloc().root_dir_entry(), PathBuf::from("/"))
219 }
220
221 pub fn entry<P: AsRef<Path>>(&self, path: P) -> io::Result<Entry> {
224 self.entry_with_path(path.as_ref())
225 }
226
227 fn entry_with_path(&self, path: &Path) -> io::Result<Entry> {
228 let names = internal::path::name_chain_from_path(path)?;
229 let path = internal::path::path_from_name_chain(&names);
230 let stream_id = match self.stream_id_for_name_chain(&names) {
231 Some(stream_id) => stream_id,
232 None => not_found!("No such object: {:?}", path),
233 };
234 Ok(Entry::new(self.minialloc().dir_entry(stream_id), path))
235 }
236
237 pub fn read_root_storage(&self) -> Entries<'_, F> {
241 let start = self.minialloc().root_dir_entry().child;
242 Entries::new(
243 EntriesOrder::Nonrecursive,
244 &self.minialloc,
245 internal::path::path_from_name_chain(&[]),
246 start,
247 )
248 }
249
250 pub fn read_storage<P: AsRef<Path>>(
252 &self,
253 path: P,
254 ) -> io::Result<Entries<'_, F>> {
255 self.read_storage_with_path(path.as_ref())
256 }
257
258 fn read_storage_with_path(
259 &self,
260 path: &Path,
261 ) -> io::Result<Entries<'_, F>> {
262 let names = internal::path::name_chain_from_path(path)?;
263 let path = internal::path::path_from_name_chain(&names);
264 let stream_id = match self.stream_id_for_name_chain(&names) {
265 Some(stream_id) => stream_id,
266 None => not_found!("No such storage: {:?}", path),
267 };
268 let start = {
269 let minialloc = self.minialloc();
270 let dir_entry = minialloc.dir_entry(stream_id);
271 if dir_entry.obj_type == ObjType::Stream {
272 invalid_input!("Not a storage: {:?}", path);
273 }
274 debug_assert!(
275 dir_entry.obj_type == ObjType::Storage
276 || dir_entry.obj_type == ObjType::Root
277 );
278 dir_entry.child
279 };
280 Ok(Entries::new(
281 EntriesOrder::Nonrecursive,
282 &self.minialloc,
283 path,
284 start,
285 ))
286 }
287
288 pub fn walk(&self) -> Entries<'_, F> {
293 Entries::new(
294 EntriesOrder::Preorder,
295 &self.minialloc,
296 internal::path::path_from_name_chain(&[]),
297 consts::ROOT_STREAM_ID,
298 )
299 }
300
301 pub fn walk_storage<P: AsRef<Path>>(
305 &self,
306 path: P,
307 ) -> io::Result<Entries<'_, F>> {
308 self.walk_storage_with_path(path.as_ref())
309 }
310
311 fn walk_storage_with_path(
312 &self,
313 path: &Path,
314 ) -> io::Result<Entries<'_, F>> {
315 let mut names = internal::path::name_chain_from_path(path)?;
316 let stream_id = match self.stream_id_for_name_chain(&names) {
317 Some(stream_id) => stream_id,
318 None => not_found!(
319 "No such object: {:?}",
320 internal::path::path_from_name_chain(&names)
321 ),
322 };
323 names.pop();
324 let parent_path = internal::path::path_from_name_chain(&names);
325 Ok(Entries::new(
326 EntriesOrder::Preorder,
327 &self.minialloc,
328 parent_path,
329 stream_id,
330 ))
331 }
332
333 pub fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
336 match internal::path::name_chain_from_path(path.as_ref()) {
337 Ok(names) => self.stream_id_for_name_chain(&names).is_some(),
338 Err(_) => false,
339 }
340 }
341
342 pub fn is_stream<P: AsRef<Path>>(&self, path: P) -> bool {
345 match internal::path::name_chain_from_path(path.as_ref()) {
346 Ok(names) => match self.stream_id_for_name_chain(&names) {
347 Some(stream_id) => {
348 self.minialloc().dir_entry(stream_id).obj_type
349 == ObjType::Stream
350 }
351 None => false,
352 },
353 Err(_) => false,
354 }
355 }
356
357 pub fn is_storage<P: AsRef<Path>>(&self, path: P) -> bool {
360 match internal::path::name_chain_from_path(path.as_ref()) {
361 Ok(names) => match self.stream_id_for_name_chain(&names) {
362 Some(stream_id) => {
363 self.minialloc().dir_entry(stream_id).obj_type
364 != ObjType::Stream
365 }
366 None => false,
367 },
368 Err(_) => false,
369 }
370 }
371
372 pub fn into_inner(self) -> F {
378 match Arc::try_unwrap(self.minialloc) {
382 Ok(ref_cell) => ref_cell.into_inner().unwrap().into_inner(),
383 Err(_) => unreachable!(),
384 }
385 }
386}
387
388impl<F: Seek> CompoundFile<F> {
389 pub fn open_stream<P: AsRef<Path>>(
392 &mut self,
393 path: P,
394 ) -> io::Result<Stream<F>> {
395 self.open_stream_with_path(path.as_ref())
396 }
397
398 fn open_stream_with_path(&mut self, path: &Path) -> io::Result<Stream<F>> {
399 let names = internal::path::name_chain_from_path(path)?;
400 let path = internal::path::path_from_name_chain(&names);
401 let stream_id = match self.stream_id_for_name_chain(&names) {
402 Some(stream_id) => stream_id,
403 None => not_found!("No such stream: {:?}", path),
404 };
405 if self.minialloc().dir_entry(stream_id).obj_type != ObjType::Stream {
406 invalid_input!("Not a stream: {:?}", path);
407 }
408 Ok(Stream::new(&self.minialloc, stream_id, self.max_buffer_size))
409 }
410}
411
412impl<F: Read + Seek> CompoundFile<F> {
413 pub fn open(inner: F) -> io::Result<CompoundFile<F>> {
417 OpenOptions::new().open_with(inner)
418 }
419
420 pub fn open_strict(inner: F) -> io::Result<CompoundFile<F>> {
426 OpenOptions::new().strict().open_with(inner)
427 }
428
429 fn open_internal(
430 mut inner: F,
431 validation: Validation,
432 max_buffer_size: usize,
433 ) -> io::Result<CompoundFile<F>> {
434 let inner_len = inner.seek(SeekFrom::End(0))?;
435 if inner_len < consts::HEADER_LEN as u64 {
436 invalid_data!(
437 "Invalid CFB file ({} bytes is too small)",
438 inner_len
439 );
440 }
441 inner.seek(SeekFrom::Start(0))?;
442
443 let header = Header::read_from(&mut inner, validation)?;
445 let sector_len = header.version.sector_len();
447 if inner_len
448 > (consts::MAX_REGULAR_SECTOR as u64 + 1) * (sector_len as u64)
449 {
450 invalid_data!(
451 "Invalid CFB file ({} bytes is too large)",
452 inner_len
453 );
454 }
455
456 if inner_len < header.version.sector_len() as u64 {
457 invalid_data!(
458 "Invalid CFB file (length of {} < sector length of {})",
459 inner_len,
460 header.version.sector_len()
461 );
462 }
463 let mut sectors = Sectors::new(header.version, inner_len, inner);
464 let num_sectors = sectors.num_sectors();
465
466 let mut difat = Vec::<u32>::new();
468 difat.extend_from_slice(&header.initial_difat_entries);
469 let mut seen_sector_ids = FnvHashSet::default();
470 let mut difat_sector_ids = Vec::new();
471 let mut current_difat_sector = header.first_difat_sector;
472 while current_difat_sector != consts::END_OF_CHAIN
473 && current_difat_sector != consts::FREE_SECTOR
474 {
475 if current_difat_sector > consts::MAX_REGULAR_SECTOR {
476 invalid_data!(
477 "DIFAT chain includes invalid sector index {}",
478 current_difat_sector
479 );
480 } else if current_difat_sector >= num_sectors {
481 invalid_data!(
482 "DIFAT chain includes sector index {}, but sector count \
483 is only {}",
484 current_difat_sector,
485 num_sectors
486 );
487 }
488 if seen_sector_ids.contains(¤t_difat_sector) {
489 invalid_data!(
490 "DIFAT chain includes duplicate sector index {}",
491 current_difat_sector,
492 );
493 }
494 seen_sector_ids.insert(current_difat_sector);
495 difat_sector_ids.push(current_difat_sector);
496 let mut sector = sectors.seek_to_sector(current_difat_sector)?;
497 for _ in 0..(sector_len / size_of::<u32>() - 1) {
498 let next = sector.read_le_u32()?;
499 if next != consts::FREE_SECTOR
500 && next > consts::MAX_REGULAR_SECTOR
501 {
502 invalid_data!(
503 "DIFAT refers to invalid sector index {}",
504 next
505 );
506 }
507 difat.push(next);
508 }
509 current_difat_sector = sector.read_le_u32()?;
510 if validation.is_strict()
511 && current_difat_sector == consts::FREE_SECTOR
512 {
513 invalid_data!(
514 "DIFAT chain must terminate with {}, not {}",
515 consts::END_OF_CHAIN,
516 consts::FREE_SECTOR
517 );
518 }
519 }
520 if validation.is_strict()
521 && header.num_difat_sectors as usize != difat_sector_ids.len()
522 {
523 invalid_data!(
524 "Incorrect DIFAT chain length (header says {}, actual is {})",
525 header.num_difat_sectors,
526 difat_sector_ids.len()
527 );
528 }
529 if !validation.is_strict() {
535 while difat.len() > consts::NUM_DIFAT_ENTRIES_IN_HEADER
536 && difat.len() > header.num_fat_sectors as usize
537 && difat.last() == Some(&0)
538 {
539 difat.pop();
540 }
541 }
542 while difat.last() == Some(&consts::FREE_SECTOR) {
543 difat.pop();
544 }
545 if validation.is_strict()
546 && header.num_fat_sectors as usize != difat.len()
547 {
548 invalid_data!(
549 "Incorrect number of FAT sectors (header says {}, DIFAT says \
550 {})",
551 header.num_fat_sectors,
552 difat.len()
553 );
554 }
555
556 let mut fat = Vec::<u32>::new();
558 for §or_index in difat.iter() {
559 if sector_index >= num_sectors {
560 invalid_data!(
561 "DIFAT refers to sector {}, but sector count is only {}",
562 sector_index,
563 num_sectors
564 );
565 }
566 let mut sector = sectors.seek_to_sector(sector_index)?;
567 for _ in 0..(sector_len / size_of::<u32>()) {
568 fat.push(sector.read_le_u32()?);
569 }
570 }
571 if !validation.is_strict() {
583 while fat.len() > num_sectors as usize {
584 if fat.last() == Some(&0)
585 || fat.last() == Some(&consts::DIFAT_SECTOR)
586 || fat.last() == Some(&consts::FAT_SECTOR)
587 || fat.last() == Some(&consts::FREE_SECTOR)
588 {
589 fat.pop();
590 } else {
591 break;
592 }
593 }
594 }
595 while fat.len() > num_sectors as usize
597 && fat.last() == Some(&consts::FREE_SECTOR)
598 {
599 fat.pop();
600 }
601 while fat.len() < num_sectors as usize {
602 fat.push(consts::FREE_SECTOR);
603 }
604
605 let mut allocator =
606 Allocator::new(sectors, difat_sector_ids, difat, fat, validation)?;
607
608 let mut dir_entries = Vec::<DirEntry>::new();
610 let mut seen_dir_sectors = FnvHashSet::default();
611 let mut current_dir_sector = header.first_dir_sector;
612 let mut dir_sector_count = 1;
613 while current_dir_sector != consts::END_OF_CHAIN {
614 if validation.is_strict()
615 && header.version == Version::V4
616 && dir_sector_count > header.num_dir_sectors
617 {
618 invalid_data!(
619 "Directory chain includes at least {} sectors which is greater than header num_dir_sectors {}",
620 dir_sector_count,
621 header.num_dir_sectors
622 );
623 }
624 if current_dir_sector > consts::MAX_REGULAR_SECTOR {
625 invalid_data!(
626 "Directory chain includes invalid sector index {}",
627 current_dir_sector
628 );
629 } else if current_dir_sector >= num_sectors {
630 invalid_data!(
631 "Directory chain includes sector index {}, but sector \
632 count is only {}",
633 current_dir_sector,
634 num_sectors
635 );
636 }
637 if seen_dir_sectors.contains(¤t_dir_sector) {
638 invalid_data!(
639 "Directory chain includes duplicate sector index {}",
640 current_dir_sector,
641 );
642 }
643 seen_dir_sectors.insert(current_dir_sector);
644 {
645 let mut sector =
646 allocator.seek_to_sector(current_dir_sector)?;
647 for _ in 0..header.version.dir_entries_per_sector() {
648 dir_entries.push(DirEntry::read_from(
649 &mut sector,
650 header.version,
651 validation,
652 )?);
653 }
654 }
655 current_dir_sector = allocator.next(current_dir_sector)?;
656 dir_sector_count += 1;
657 }
658
659 let mut directory = Directory::new(
660 allocator,
661 dir_entries,
662 header.first_dir_sector,
663 validation,
664 )?;
665
666 let minifat = {
668 let mut chain = directory
669 .open_chain(header.first_minifat_sector, SectorInit::Fat)?;
670 if validation.is_strict()
671 && header.num_minifat_sectors as usize != chain.num_sectors()
672 {
673 invalid_data!(
674 "Incorrect MiniFAT chain length (header says {}, actual \
675 is {})",
676 header.num_minifat_sectors,
677 chain.num_sectors()
678 );
679 }
680 let num_minifat_entries = (chain.len() / 4) as usize;
681 let mut minifat = Vec::<u32>::with_capacity(num_minifat_entries);
682 for _ in 0..num_minifat_entries {
683 minifat.push(chain.read_le_u32()?);
684 }
685 while minifat.last() == Some(&consts::FREE_SECTOR) {
686 minifat.pop();
687 }
688 minifat
689 };
690
691 let minialloc = MiniAllocator::new(
692 directory,
693 minifat,
694 header.first_minifat_sector,
695 validation,
696 )?;
697
698 Ok(CompoundFile {
699 minialloc: Arc::new(RwLock::new(minialloc)),
700 max_buffer_size,
701 })
702 }
703}
704
705impl<F: Read + Write + Seek> CompoundFile<F> {
706 pub fn create(inner: F) -> io::Result<CompoundFile<F>> {
709 OpenOptions::new().create_with(inner)
710 }
711
712 pub fn create_with_version(
715 version: Version,
716 inner: F,
717 ) -> io::Result<CompoundFile<F>> {
718 CompoundFile::create_with_version_and_options(
719 version,
720 inner,
721 DEFAULT_STREAM_MAX_BUFFER_SIZE,
722 )
723 }
724
725 fn create_with_version_and_options(
726 version: Version,
727 mut inner: F,
728 max_buffer_size: usize,
729 ) -> io::Result<CompoundFile<F>> {
730 let mut header = Header {
731 version,
732 num_dir_sectors: if version == Version::V3 { 0 } else { 1 },
734 num_fat_sectors: 1,
735 first_dir_sector: 1,
736 first_minifat_sector: consts::END_OF_CHAIN,
737 num_minifat_sectors: 0,
738 first_difat_sector: consts::END_OF_CHAIN,
739 num_difat_sectors: 0,
740 initial_difat_entries: [consts::FREE_SECTOR;
741 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
742 };
743 header.initial_difat_entries[0] = 0;
744 header.write_to(&mut inner)?;
745
746 let sector_len = version.sector_len();
748 debug_assert!(sector_len >= consts::HEADER_LEN);
749 if sector_len > consts::HEADER_LEN {
750 inner.write_all(&vec![0; sector_len - consts::HEADER_LEN])?;
751 }
752
753 let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
755 for &entry in fat.iter() {
756 inner.write_le_u32(entry)?;
757 }
758 for _ in fat.len()..(sector_len / size_of::<u32>()) {
759 inner.write_le_u32(consts::FREE_SECTOR)?;
760 }
761 let difat: Vec<u32> = vec![0];
762 let difat_sector_ids: Vec<u32> = vec![];
763
764 let root_dir_entry = DirEntry::empty_root_entry();
766 root_dir_entry.write_to(&mut inner)?;
767 for _ in 1..version.dir_entries_per_sector() {
768 DirEntry::unallocated().write_to(&mut inner)?;
769 }
770
771 let sectors = Sectors::new(version, 3 * sector_len as u64, inner);
772 let allocator = Allocator::new(
773 sectors,
774 difat_sector_ids,
775 difat,
776 fat,
777 Validation::Strict,
778 )?;
779 let directory = Directory::new(
780 allocator,
781 vec![root_dir_entry],
782 1,
783 Validation::Strict,
784 )?;
785 let minialloc = MiniAllocator::new(
786 directory,
787 vec![],
788 consts::END_OF_CHAIN,
789 Validation::Strict,
790 )?;
791 Ok(CompoundFile {
792 minialloc: Arc::new(RwLock::new(minialloc)),
793 max_buffer_size,
794 })
795 }
796
797 pub fn create_storage<P: AsRef<Path>>(
800 &mut self,
801 path: P,
802 ) -> io::Result<()> {
803 self.create_storage_with_path(path.as_ref())
804 }
805
806 fn create_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
807 let mut names = internal::path::name_chain_from_path(path)?;
808 if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
809 let path = internal::path::path_from_name_chain(&names);
810 if self.minialloc().dir_entry(stream_id).obj_type
811 != ObjType::Stream
812 {
813 already_exists!(
814 "Cannot create storage at {:?} because a \
815 storage already exists there",
816 path
817 );
818 } else {
819 already_exists!(
820 "Cannot create storage at {:?} because a \
821 stream already exists there",
822 path
823 );
824 }
825 }
826 debug_assert!(!names.is_empty());
829 let name = names.pop().unwrap();
830 let parent_id = match self.stream_id_for_name_chain(&names) {
831 Some(stream_id) => stream_id,
832 None => not_found!("Parent storage doesn't exist"),
833 };
834 self.minialloc_mut().insert_dir_entry(
835 parent_id,
836 name,
837 ObjType::Storage,
838 )?;
839 Ok(())
840 }
841
842 pub fn create_storage_all<P: AsRef<Path>>(
845 &mut self,
846 path: P,
847 ) -> io::Result<()> {
848 self.create_storage_all_with_path(path.as_ref())
849 }
850
851 fn create_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
852 let names = internal::path::name_chain_from_path(path)?;
853 for length in 1..(names.len() + 1) {
854 let prefix_path =
855 internal::path::path_from_name_chain(&names[..length]);
856 if self.is_storage(&prefix_path) {
857 continue;
858 }
859 self.create_storage_with_path(&prefix_path)?;
860 }
861 Ok(())
862 }
863
864 pub fn remove_storage<P: AsRef<Path>>(
867 &mut self,
868 path: P,
869 ) -> io::Result<()> {
870 self.remove_storage_with_path(path.as_ref())
871 }
872
873 fn remove_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
874 let mut names = internal::path::name_chain_from_path(path)?;
875 let stream_id = match self.stream_id_for_name_chain(&names) {
876 Some(parent_id) => parent_id,
877 None => not_found!("No such storage: {:?}", path),
878 };
879 {
880 let minialloc = self.minialloc();
881 let dir_entry = minialloc.dir_entry(stream_id);
882 if dir_entry.obj_type == ObjType::Root {
883 invalid_input!("Cannot remove the root storage object");
884 }
885 if dir_entry.obj_type == ObjType::Stream {
886 invalid_input!("Not a storage: {:?}", path);
887 }
888 debug_assert_eq!(dir_entry.obj_type, ObjType::Storage);
889 if dir_entry.child != consts::NO_STREAM {
890 invalid_input!("Storage is not empty: {:?}", path);
891 }
892 }
893 debug_assert!(!names.is_empty());
894 let name = names.pop().unwrap();
895 let parent_id = self.stream_id_for_name_chain(&names).unwrap();
896 self.minialloc_mut().remove_dir_entry(parent_id, name)?;
897 Ok(())
898 }
899
900 pub fn remove_storage_all<P: AsRef<Path>>(
904 &mut self,
905 path: P,
906 ) -> io::Result<()> {
907 self.remove_storage_all_with_path(path.as_ref())
908 }
909
910 fn remove_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
911 let mut stack = self.walk_storage(path)?.collect::<Vec<Entry>>();
912 while let Some(entry) = stack.pop() {
913 if entry.is_stream() {
914 self.remove_stream_with_path(entry.path())?;
915 } else if !entry.is_root() {
916 self.remove_storage_with_path(entry.path())?;
917 }
918 }
919 Ok(())
920 }
921
922 pub fn set_storage_clsid<P: AsRef<Path>>(
926 &mut self,
927 path: P,
928 clsid: Uuid,
929 ) -> io::Result<()> {
930 self.set_storage_clsid_with_path(path.as_ref(), clsid)
931 }
932
933 fn set_storage_clsid_with_path(
934 &mut self,
935 path: &Path,
936 clsid: Uuid,
937 ) -> io::Result<()> {
938 let names = internal::path::name_chain_from_path(path)?;
939 let stream_id = match self.stream_id_for_name_chain(&names) {
940 Some(stream_id) => stream_id,
941 None => not_found!(
942 "No such storage: {:?}",
943 internal::path::path_from_name_chain(&names)
944 ),
945 };
946 let mut minialloc = self.minialloc_mut();
947 if minialloc.dir_entry(stream_id).obj_type == ObjType::Stream {
948 invalid_input!(
949 "Not a storage: {:?}",
950 internal::path::path_from_name_chain(&names)
951 );
952 }
953 minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
954 dir_entry.clsid = clsid;
955 })
956 }
957
958 pub fn create_stream<P: AsRef<Path>>(
962 &mut self,
963 path: P,
964 ) -> io::Result<Stream<F>> {
965 self.create_stream_with_path(path.as_ref(), true)
966 }
967
968 pub fn create_new_stream<P: AsRef<Path>>(
972 &mut self,
973 path: P,
974 ) -> io::Result<Stream<F>> {
975 self.create_stream_with_path(path.as_ref(), false)
976 }
977
978 fn create_stream_with_path(
979 &mut self,
980 path: &Path,
981 overwrite: bool,
982 ) -> io::Result<Stream<F>> {
983 let mut names = internal::path::name_chain_from_path(path)?;
984 if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
985 if self.minialloc().dir_entry(stream_id).obj_type
986 != ObjType::Stream
987 {
988 already_exists!(
989 "Cannot create stream at {:?} because a \
990 storage already exists there",
991 internal::path::path_from_name_chain(&names)
992 );
993 } else if !overwrite {
994 already_exists!(
995 "Cannot create new stream at {:?} because a \
996 stream already exists there",
997 internal::path::path_from_name_chain(&names)
998 );
999 } else {
1000 let mut stream = Stream::new(
1001 &self.minialloc,
1002 stream_id,
1003 self.max_buffer_size,
1004 );
1005 stream.set_len(0)?;
1006 return Ok(stream);
1007 }
1008 }
1009 debug_assert!(!names.is_empty());
1012 let name = names.pop().unwrap();
1013 let parent_id = match self.stream_id_for_name_chain(&names) {
1014 Some(stream_id) => stream_id,
1015 None => not_found!("Parent storage doesn't exist"),
1016 };
1017 let new_stream_id = self.minialloc_mut().insert_dir_entry(
1018 parent_id,
1019 name,
1020 ObjType::Stream,
1021 )?;
1022 Ok(Stream::new(&self.minialloc, new_stream_id, self.max_buffer_size))
1023 }
1024
1025 pub fn remove_stream<P: AsRef<Path>>(
1027 &mut self,
1028 path: P,
1029 ) -> io::Result<()> {
1030 self.remove_stream_with_path(path.as_ref())
1031 }
1032
1033 fn remove_stream_with_path(&mut self, path: &Path) -> io::Result<()> {
1034 let mut names = internal::path::name_chain_from_path(path)?;
1035 let stream_id = match self.stream_id_for_name_chain(&names) {
1036 Some(parent_id) => parent_id,
1037 None => not_found!("No such stream: {:?}", path),
1038 };
1039 let (start_sector_id, is_in_mini_stream) = {
1040 let minialloc = self.minialloc();
1041 let dir_entry = minialloc.dir_entry(stream_id);
1042 if dir_entry.obj_type != ObjType::Stream {
1043 invalid_input!("Not a stream: {:?}", path);
1044 }
1045 debug_assert_eq!(dir_entry.child, consts::NO_STREAM);
1046 (
1047 dir_entry.start_sector,
1048 dir_entry.stream_len < consts::MINI_STREAM_CUTOFF as u64,
1049 )
1050 };
1051 if is_in_mini_stream {
1052 self.minialloc_mut().free_mini_chain(start_sector_id)?;
1053 } else {
1054 self.minialloc_mut().free_chain(start_sector_id)?;
1055 }
1056 debug_assert!(!names.is_empty());
1057 let name = names.pop().unwrap();
1058 let parent_id = self.stream_id_for_name_chain(&names).unwrap();
1059 self.minialloc_mut().remove_dir_entry(parent_id, name)?;
1060 Ok(())
1061 }
1062
1063 pub fn set_state_bits<P: AsRef<Path>>(
1067 &mut self,
1068 path: P,
1069 bits: u32,
1070 ) -> io::Result<()> {
1071 self.set_entry_with_path(path.as_ref(), |dir_entry| {
1072 dir_entry.state_bits = bits
1073 })
1074 }
1075
1076 pub fn touch<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
1079 self.set_modified_time(path, web_time::SystemTime::now())
1080 }
1081
1082 pub fn set_modified_time<P: AsRef<Path>>(
1085 &mut self,
1086 path: P,
1087 ts: web_time::SystemTime,
1088 ) -> io::Result<()> {
1089 self.set_entry_with_path(path.as_ref(), |dir_entry| {
1090 if dir_entry.obj_type != ObjType::Stream {
1091 dir_entry.modified_time = Timestamp::from_system_time(ts);
1092 }
1093 })
1094 }
1095
1096 pub fn set_created_time<P: AsRef<Path>>(
1099 &mut self,
1100 path: P,
1101 ts: web_time::SystemTime,
1102 ) -> io::Result<()> {
1103 self.set_entry_with_path(path.as_ref(), |dir_entry| {
1104 if dir_entry.obj_type != ObjType::Stream {
1105 dir_entry.creation_time = Timestamp::from_system_time(ts);
1106 }
1107 })
1108 }
1109
1110 fn set_entry_with_path<G: FnMut(&mut DirEntry)>(
1111 &mut self,
1112 path: &Path,
1113 f: G,
1114 ) -> io::Result<()> {
1115 let names = internal::path::name_chain_from_path(path)?;
1116 let path = internal::path::path_from_name_chain(&names);
1117 let stream_id = match self.stream_id_for_name_chain(&names) {
1118 Some(stream_id) => stream_id,
1119 None => not_found!("No such object: {:?}", path),
1120 };
1121 self.minialloc_mut().with_dir_entry_mut(stream_id, f)?;
1122 Ok(())
1123 }
1124
1125 pub fn flush(&mut self) -> io::Result<()> {
1127 self.minialloc_mut().flush()
1128 }
1129}
1130
1131impl<F: fmt::Debug> fmt::Debug for CompoundFile<F> {
1132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1133 f.debug_tuple("CompoundFile").field(self.minialloc().inner()).finish()
1134 }
1135}
1136
1137trait ReadLeNumber: Read {
1138 fn read_le_u64(&mut self) -> Result<u64, std::io::Error> {
1139 let mut buf = [0u8; 8];
1140 self.read_exact(&mut buf)?;
1141 Ok(u64::from_le_bytes(buf))
1142 }
1143 fn read_le_u32(&mut self) -> Result<u32, std::io::Error> {
1144 let mut buf = [0u8; 4];
1145 self.read_exact(&mut buf)?;
1146 Ok(u32::from_le_bytes(buf))
1147 }
1148 fn read_le_u16(&mut self) -> Result<u16, std::io::Error> {
1149 let mut buf = [0u8; 2];
1150 self.read_exact(&mut buf)?;
1151 Ok(u16::from_le_bytes(buf))
1152 }
1153}
1154impl<T: Read> ReadLeNumber for T {}
1155
1156trait WriteLeNumber: Write {
1157 fn write_le_u64(&mut self, num: u64) -> Result<(), std::io::Error> {
1158 self.write_all(&num.to_le_bytes())
1159 }
1160 fn write_le_u32(&mut self, num: u32) -> Result<(), std::io::Error> {
1161 self.write_all(&num.to_le_bytes())
1162 }
1163 fn write_le_u16(&mut self, num: u16) -> Result<(), std::io::Error> {
1164 self.write_all(&num.to_le_bytes())
1165 }
1166}
1167impl<T: Write> WriteLeNumber for T {}
1168#[cfg(test)]
1171mod tests {
1172 use std::io::{self, Cursor, Seek, SeekFrom};
1173 use std::mem::size_of;
1174 use std::path::Path;
1175
1176 use crate::internal::{
1177 consts, DirEntry, Header, ObjType, Timestamp, Version,
1178 };
1179 use crate::{ReadLeNumber, WriteLeNumber};
1180
1181 use super::CompoundFile;
1182
1183 fn make_cfb_file_with_zero_padded_fat() -> io::Result<Vec<u8>> {
1184 let version = Version::V3;
1185 let mut data = Vec::<u8>::new();
1186 let mut header = Header {
1187 version,
1188 num_dir_sectors: 0,
1189 num_fat_sectors: 1,
1190 first_dir_sector: 1,
1191 first_minifat_sector: consts::END_OF_CHAIN,
1192 num_minifat_sectors: 0,
1193 first_difat_sector: consts::END_OF_CHAIN,
1194 num_difat_sectors: 0,
1195 initial_difat_entries: [consts::FREE_SECTOR;
1196 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
1197 };
1198 header.initial_difat_entries[0] = 0;
1199 header.write_to(&mut data)?;
1200 let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
1202 for &entry in fat.iter() {
1203 data.write_le_u32(entry)?;
1204 }
1205 for _ in fat.len()..(version.sector_len() / size_of::<u32>()) {
1209 data.write_le_u32(0)?;
1210 }
1211 DirEntry::empty_root_entry().write_to(&mut data)?;
1213 for _ in 1..version.dir_entries_per_sector() {
1214 DirEntry::unallocated().write_to(&mut data)?;
1215 }
1216 Ok(data)
1217 }
1218
1219 fn make_cfb_with_ts(ts: web_time::SystemTime) -> Vec<u8> {
1220 use std::io::Write;
1221
1222 let mut buf = Vec::new();
1223 let mut cfb = CompoundFile::create(io::Cursor::new(&mut buf)).unwrap();
1224
1225 cfb.create_storage("/foo/").unwrap();
1226 let mut stream = cfb.create_stream("/foo/bar").unwrap();
1227 stream.write_all(b"data").unwrap();
1228 drop(stream);
1229
1230 let entries: Vec<_> = cfb.walk().collect();
1231 for entr in entries {
1232 cfb.set_modified_time(entr.path(), ts).unwrap();
1233 cfb.set_created_time(entr.path(), ts).unwrap();
1234 }
1235 cfb.flush().unwrap();
1236 buf
1237 }
1238
1239 #[test]
1240 fn zero_padded_fat_strict() {
1241 let data = make_cfb_file_with_zero_padded_fat().unwrap();
1242 let result = CompoundFile::open_strict(Cursor::new(data));
1243 assert_eq!(
1244 result.err().unwrap().to_string(),
1245 "Malformed FAT (FAT has 128 entries, but file has only 2 sectors)"
1246 );
1247 }
1248
1249 #[test]
1251 fn zero_padded_fat_permissive() {
1252 let data = make_cfb_file_with_zero_padded_fat().unwrap();
1253 CompoundFile::open(Cursor::new(data)).expect("open");
1256 }
1257
1258 fn make_cfb_file_with_zero_padded_difat() -> io::Result<Vec<u8>> {
1259 let version = Version::V3;
1260 let mut data = Vec::<u8>::new();
1261
1262 let dir_sector = 0;
1263 let difat_sector = 1;
1264 let num_fat_sectors = consts::NUM_DIFAT_ENTRIES_IN_HEADER + 1;
1266 let first_fat_sector = difat_sector + 1;
1268 let fat_sectors: Vec<u32> = (0..num_fat_sectors)
1269 .map(|i| (first_fat_sector + i) as u32)
1270 .collect();
1271
1272 let header = Header {
1274 version,
1275 num_dir_sectors: 0,
1276 num_fat_sectors: num_fat_sectors as u32,
1277 first_dir_sector: dir_sector as u32,
1278 first_minifat_sector: consts::END_OF_CHAIN,
1279 num_minifat_sectors: 0,
1280 first_difat_sector: difat_sector as u32,
1281 num_difat_sectors: 1,
1282 initial_difat_entries: std::array::from_fn(|difat_entry_i| {
1283 fat_sectors[difat_entry_i]
1284 }),
1285 };
1286 header.write_to(&mut data)?;
1287
1288 DirEntry::empty_root_entry().write_to(&mut data)?;
1290 for _ in 1..version.dir_entries_per_sector() {
1291 DirEntry::unallocated().write_to(&mut data)?;
1292 }
1293
1294 let num_difat_entries_in_sector =
1296 version.sector_len() / size_of::<u32>() - 1;
1297 for i in 0..num_difat_entries_in_sector {
1298 let difat_entry_i = i + consts::NUM_DIFAT_ENTRIES_IN_HEADER;
1299
1300 let entry = if difat_entry_i < num_fat_sectors {
1301 fat_sectors[difat_entry_i]
1302 } else {
1303 0
1306 };
1307 data.write_le_u32(entry)?;
1308 }
1309 data.write_le_u32(consts::END_OF_CHAIN)?;
1311
1312 let num_fat_entries_in_sector =
1314 version.sector_len() / size_of::<u32>();
1315 let mut fat = vec![consts::FREE_SECTOR; num_fat_entries_in_sector * 2];
1316 fat[difat_sector] = consts::DIFAT_SECTOR;
1317 fat[dir_sector] = consts::END_OF_CHAIN;
1318 for fat_sector in fat_sectors {
1319 fat[fat_sector as usize] = consts::FAT_SECTOR;
1320 }
1321 for entry in fat {
1322 data.write_le_u32(entry)?;
1323 }
1324
1325 for _fat_sector in 2..num_fat_sectors {
1327 for _i in 0..num_fat_entries_in_sector {
1328 data.write_le_u32(consts::FREE_SECTOR)?;
1329 }
1330 }
1331
1332 Ok(data)
1333 }
1334
1335 #[test]
1336 fn zero_padded_difat_strict() {
1337 let data = make_cfb_file_with_zero_padded_difat().unwrap();
1338 let result = CompoundFile::open_strict(Cursor::new(data));
1339 assert_eq!(
1340 result.err().unwrap().to_string(),
1341 "Incorrect number of FAT sectors (header says 110, DIFAT says 236)",
1342 );
1343 }
1344
1345 #[test]
1347 fn zero_padded_difat_permissive() {
1348 let data = make_cfb_file_with_zero_padded_difat().unwrap();
1349 CompoundFile::open(Cursor::new(data)).expect("open");
1352 }
1353
1354 #[test]
1356 fn update_num_dir_sectors() {
1357 let cursor = Cursor::new(Vec::new());
1359 let mut comp = CompoundFile::create(cursor).unwrap();
1360 for i in 0..32 {
1363 let path = format!("stream{i}");
1364 let path = Path::new(&path);
1365 comp.create_stream(path).unwrap();
1366 }
1367 comp.flush().unwrap();
1368
1369 let mut cursor = comp.into_inner();
1371 cursor.seek(SeekFrom::Start(40)).unwrap();
1372 let num_dir_sectors = cursor.read_le_u32().unwrap();
1373 assert_eq!(num_dir_sectors, 2);
1374 }
1375
1376 #[test]
1377 fn deterministic_cfbs() {
1378 let ts = web_time::SystemTime::now();
1379 let cfb1 = make_cfb_with_ts(ts);
1380 let cfb2 = make_cfb_with_ts(ts);
1381 let ts = Timestamp::from_system_time(ts);
1382 assert_eq!(cfb1, cfb2);
1383
1384 let cfb = CompoundFile::open(Cursor::new(&cfb1)).unwrap();
1385
1386 let entry = cfb.entry("/foo").unwrap();
1387 assert_eq!(Timestamp::from_system_time(entry.created()), ts);
1388 assert_eq!(Timestamp::from_system_time(entry.modified()), ts);
1389
1390 let strict = CompoundFile::open_strict(Cursor::new(cfb1)).unwrap();
1391
1392 let entry = strict.entry("/foo").unwrap();
1393 assert_eq!(Timestamp::from_system_time(entry.created()), ts);
1394 assert_eq!(Timestamp::from_system_time(entry.modified()), ts);
1395 }
1396 fn make_cfb_with_inconsistent_difat_entries() -> io::Result<Vec<u8>> {
1397 let mut data = Vec::new();
1398 let mut hdr = Header {
1400 version: Version::V3,
1401 num_dir_sectors: 0,
1402 num_fat_sectors: 1,
1403 first_dir_sector: 0,
1404 first_minifat_sector: consts::END_OF_CHAIN,
1405 num_minifat_sectors: 0,
1406 first_difat_sector: consts::END_OF_CHAIN,
1407 num_difat_sectors: 0,
1408 initial_difat_entries: [consts::FREE_SECTOR;
1409 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
1410 };
1411 hdr.initial_difat_entries[0] = 1;
1412
1413 hdr.write_to(&mut data)?;
1414
1415 for entr in [
1417 DirEntry::new("Root Entry", ObjType::Root, Timestamp::now()),
1418 DirEntry::unallocated(),
1419 DirEntry::unallocated(),
1420 DirEntry::unallocated(),
1421 ] {
1422 entr.write_to(&mut data)?;
1423 }
1424
1425 data.extend(&consts::END_OF_CHAIN.to_le_bytes());
1427 data.extend(&consts::FAT_SECTOR.to_le_bytes());
1428 data.extend(&consts::DIFAT_SECTOR.to_le_bytes());
1430 for _ in (0..128).skip(3) {
1431 data.extend(&consts::FREE_SECTOR.to_le_bytes());
1432 }
1433
1434 Ok(data)
1435 }
1436
1437 #[test]
1438 fn too_many_fat_entries() {
1439 use std::io::Write;
1440
1441 let cfb = make_cfb_with_inconsistent_difat_entries().unwrap();
1442
1443 let mut cfb = CompoundFile::open(Cursor::new(cfb)).unwrap();
1444 let mut f = cfb.create_stream("stream").unwrap();
1445 f.write_all(&vec![0; 1024 * 1024]).unwrap();
1446 }
1447}
1448
1449