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::{
60 Allocator, DirEntry, Directory, EntriesOrder, Header, MiniAllocator,
61 ObjType, SectorInit, Sectors, Timestamp, Validation,
62};
63pub use crate::internal::{Entries, Entry, Stream, Version};
64
65#[macro_use]
66mod internal;
67
68pub fn open<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
72 CompoundFile::open(fs::File::open(path)?)
73}
74
75pub fn open_rw<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
77 open_rw_with_path(path.as_ref())
78}
79
80fn open_rw_with_path(path: &Path) -> io::Result<CompoundFile<fs::File>> {
81 let file = fs::OpenOptions::new().read(true).write(true).open(path)?;
82 CompoundFile::open(file)
83}
84
85pub fn create<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
90 create_with_path(path.as_ref())
91}
92
93fn create_with_path(path: &Path) -> io::Result<CompoundFile<fs::File>> {
94 let file = fs::OpenOptions::new()
95 .read(true)
96 .write(true)
97 .create(true)
98 .truncate(true)
99 .open(path)?;
100 CompoundFile::create(file)
101}
102
103pub struct CompoundFile<F> {
109 minialloc: Arc<RwLock<MiniAllocator<F>>>,
110}
111
112impl<F> CompoundFile<F> {
113 fn minialloc(&self) -> RwLockReadGuard<'_, MiniAllocator<F>> {
114 self.minialloc.read().unwrap()
115 }
116
117 fn minialloc_mut(&mut self) -> RwLockWriteGuard<'_, MiniAllocator<F>> {
118 self.minialloc.write().unwrap()
119 }
120
121 pub fn version(&self) -> Version {
123 self.minialloc().version()
124 }
125
126 fn stream_id_for_name_chain(&self, names: &[&str]) -> Option<u32> {
127 self.minialloc().stream_id_for_name_chain(names)
128 }
129
130 pub fn root_entry(&self) -> Entry {
133 Entry::new(self.minialloc().root_dir_entry(), PathBuf::from("/"))
134 }
135
136 pub fn entry<P: AsRef<Path>>(&self, path: P) -> io::Result<Entry> {
139 self.entry_with_path(path.as_ref())
140 }
141
142 fn entry_with_path(&self, path: &Path) -> io::Result<Entry> {
143 let names = internal::path::name_chain_from_path(path)?;
144 let path = internal::path::path_from_name_chain(&names);
145 let stream_id = match self.stream_id_for_name_chain(&names) {
146 Some(stream_id) => stream_id,
147 None => not_found!("No such object: {:?}", path),
148 };
149 Ok(Entry::new(self.minialloc().dir_entry(stream_id), path))
150 }
151
152 pub fn read_root_storage(&self) -> Entries<'_, F> {
156 let start = self.minialloc().root_dir_entry().child;
157 Entries::new(
158 EntriesOrder::Nonrecursive,
159 &self.minialloc,
160 internal::path::path_from_name_chain(&[]),
161 start,
162 )
163 }
164
165 pub fn read_storage<P: AsRef<Path>>(
167 &self,
168 path: P,
169 ) -> io::Result<Entries<'_, F>> {
170 self.read_storage_with_path(path.as_ref())
171 }
172
173 fn read_storage_with_path(
174 &self,
175 path: &Path,
176 ) -> io::Result<Entries<'_, F>> {
177 let names = internal::path::name_chain_from_path(path)?;
178 let path = internal::path::path_from_name_chain(&names);
179 let stream_id = match self.stream_id_for_name_chain(&names) {
180 Some(stream_id) => stream_id,
181 None => not_found!("No such storage: {:?}", path),
182 };
183 let start = {
184 let minialloc = self.minialloc();
185 let dir_entry = minialloc.dir_entry(stream_id);
186 if dir_entry.obj_type == ObjType::Stream {
187 invalid_input!("Not a storage: {:?}", path);
188 }
189 debug_assert!(
190 dir_entry.obj_type == ObjType::Storage
191 || dir_entry.obj_type == ObjType::Root
192 );
193 dir_entry.child
194 };
195 Ok(Entries::new(
196 EntriesOrder::Nonrecursive,
197 &self.minialloc,
198 path,
199 start,
200 ))
201 }
202
203 pub fn walk(&self) -> Entries<'_, F> {
208 Entries::new(
209 EntriesOrder::Preorder,
210 &self.minialloc,
211 internal::path::path_from_name_chain(&[]),
212 consts::ROOT_STREAM_ID,
213 )
214 }
215
216 pub fn walk_storage<P: AsRef<Path>>(
220 &self,
221 path: P,
222 ) -> io::Result<Entries<'_, F>> {
223 self.walk_storage_with_path(path.as_ref())
224 }
225
226 fn walk_storage_with_path(
227 &self,
228 path: &Path,
229 ) -> io::Result<Entries<'_, F>> {
230 let mut names = internal::path::name_chain_from_path(path)?;
231 let stream_id = match self.stream_id_for_name_chain(&names) {
232 Some(stream_id) => stream_id,
233 None => not_found!(
234 "No such object: {:?}",
235 internal::path::path_from_name_chain(&names)
236 ),
237 };
238 names.pop();
239 let parent_path = internal::path::path_from_name_chain(&names);
240 Ok(Entries::new(
241 EntriesOrder::Preorder,
242 &self.minialloc,
243 parent_path,
244 stream_id,
245 ))
246 }
247
248 pub fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
251 match internal::path::name_chain_from_path(path.as_ref()) {
252 Ok(names) => self.stream_id_for_name_chain(&names).is_some(),
253 Err(_) => false,
254 }
255 }
256
257 pub fn is_stream<P: AsRef<Path>>(&self, path: P) -> bool {
260 match internal::path::name_chain_from_path(path.as_ref()) {
261 Ok(names) => match self.stream_id_for_name_chain(&names) {
262 Some(stream_id) => {
263 self.minialloc().dir_entry(stream_id).obj_type
264 == ObjType::Stream
265 }
266 None => false,
267 },
268 Err(_) => false,
269 }
270 }
271
272 pub fn is_storage<P: AsRef<Path>>(&self, path: P) -> bool {
275 match internal::path::name_chain_from_path(path.as_ref()) {
276 Ok(names) => match self.stream_id_for_name_chain(&names) {
277 Some(stream_id) => {
278 self.minialloc().dir_entry(stream_id).obj_type
279 != ObjType::Stream
280 }
281 None => false,
282 },
283 Err(_) => false,
284 }
285 }
286
287 pub fn into_inner(self) -> F {
293 match Arc::try_unwrap(self.minialloc) {
297 Ok(ref_cell) => ref_cell.into_inner().unwrap().into_inner(),
298 Err(_) => unreachable!(),
299 }
300 }
301}
302
303impl<F: Seek> CompoundFile<F> {
304 pub fn open_stream<P: AsRef<Path>>(
307 &mut self,
308 path: P,
309 ) -> io::Result<Stream<F>> {
310 self.open_stream_with_path(path.as_ref())
311 }
312
313 fn open_stream_with_path(&mut self, path: &Path) -> io::Result<Stream<F>> {
314 let names = internal::path::name_chain_from_path(path)?;
315 let path = internal::path::path_from_name_chain(&names);
316 let stream_id = match self.stream_id_for_name_chain(&names) {
317 Some(stream_id) => stream_id,
318 None => not_found!("No such stream: {:?}", path),
319 };
320 if self.minialloc().dir_entry(stream_id).obj_type != ObjType::Stream {
321 invalid_input!("Not a stream: {:?}", path);
322 }
323 Ok(Stream::new(&self.minialloc, stream_id))
324 }
325}
326
327impl<F: Read + Seek> CompoundFile<F> {
328 pub fn open(inner: F) -> io::Result<CompoundFile<F>> {
332 CompoundFile::open_internal(inner, Validation::Permissive)
333 }
334
335 pub fn open_strict(inner: F) -> io::Result<CompoundFile<F>> {
341 CompoundFile::open_internal(inner, Validation::Strict)
342 }
343
344 fn open_internal(
345 mut inner: F,
346 validation: Validation,
347 ) -> io::Result<CompoundFile<F>> {
348 let inner_len = inner.seek(SeekFrom::End(0))?;
349 if inner_len < consts::HEADER_LEN as u64 {
350 invalid_data!(
351 "Invalid CFB file ({} bytes is too small)",
352 inner_len
353 );
354 }
355 inner.seek(SeekFrom::Start(0))?;
356
357 let header = Header::read_from(&mut inner, validation)?;
359 let sector_len = header.version.sector_len();
361 if inner_len
362 > (consts::MAX_REGULAR_SECTOR as u64 + 1) * (sector_len as u64)
363 {
364 invalid_data!(
365 "Invalid CFB file ({} bytes is too large)",
366 inner_len
367 );
368 }
369
370 if inner_len < header.version.sector_len() as u64 {
371 invalid_data!(
372 "Invalid CFB file (length of {} < sector length of {})",
373 inner_len,
374 header.version.sector_len()
375 );
376 }
377 let mut sectors = Sectors::new(header.version, inner_len, inner);
378 let num_sectors = sectors.num_sectors();
379
380 let mut difat = Vec::<u32>::new();
382 difat.extend_from_slice(&header.initial_difat_entries);
383 let mut seen_sector_ids = FnvHashSet::default();
384 let mut difat_sector_ids = Vec::new();
385 let mut current_difat_sector = header.first_difat_sector;
386 while current_difat_sector != consts::END_OF_CHAIN
387 && current_difat_sector != consts::FREE_SECTOR
388 {
389 if current_difat_sector > consts::MAX_REGULAR_SECTOR {
390 invalid_data!(
391 "DIFAT chain includes invalid sector index {}",
392 current_difat_sector
393 );
394 } else if current_difat_sector >= num_sectors {
395 invalid_data!(
396 "DIFAT chain includes sector index {}, but sector count \
397 is only {}",
398 current_difat_sector,
399 num_sectors
400 );
401 }
402 if seen_sector_ids.contains(¤t_difat_sector) {
403 invalid_data!(
404 "DIFAT chain includes duplicate sector index {}",
405 current_difat_sector,
406 );
407 }
408 seen_sector_ids.insert(current_difat_sector);
409 difat_sector_ids.push(current_difat_sector);
410 let mut sector = sectors.seek_to_sector(current_difat_sector)?;
411 for _ in 0..(sector_len / size_of::<u32>() - 1) {
412 let next = sector.read_le_u32()?;
413 if next != consts::FREE_SECTOR
414 && next > consts::MAX_REGULAR_SECTOR
415 {
416 invalid_data!(
417 "DIFAT refers to invalid sector index {}",
418 next
419 );
420 }
421 difat.push(next);
422 }
423 current_difat_sector = sector.read_le_u32()?;
424 if validation.is_strict()
425 && current_difat_sector == consts::FREE_SECTOR
426 {
427 invalid_data!(
428 "DIFAT chain must terminate with {}, not {}",
429 consts::END_OF_CHAIN,
430 consts::FREE_SECTOR
431 );
432 }
433 }
434 if validation.is_strict()
435 && header.num_difat_sectors as usize != difat_sector_ids.len()
436 {
437 invalid_data!(
438 "Incorrect DIFAT chain length (header says {}, actual is {})",
439 header.num_difat_sectors,
440 difat_sector_ids.len()
441 );
442 }
443 if !validation.is_strict() {
449 while difat.len() > consts::NUM_DIFAT_ENTRIES_IN_HEADER
450 && difat.len() > header.num_fat_sectors as usize
451 && difat.last() == Some(&0)
452 {
453 difat.pop();
454 }
455 }
456 while difat.last() == Some(&consts::FREE_SECTOR) {
457 difat.pop();
458 }
459 if validation.is_strict()
460 && header.num_fat_sectors as usize != difat.len()
461 {
462 invalid_data!(
463 "Incorrect number of FAT sectors (header says {}, DIFAT says \
464 {})",
465 header.num_fat_sectors,
466 difat.len()
467 );
468 }
469
470 let mut fat = Vec::<u32>::new();
472 for §or_index in difat.iter() {
473 if sector_index >= num_sectors {
474 invalid_data!(
475 "DIFAT refers to sector {}, but sector count is only {}",
476 sector_index,
477 num_sectors
478 );
479 }
480 let mut sector = sectors.seek_to_sector(sector_index)?;
481 for _ in 0..(sector_len / size_of::<u32>()) {
482 fat.push(sector.read_le_u32()?);
483 }
484 }
485 if !validation.is_strict() {
495 while fat.len() > num_sectors as usize && fat.last() == Some(&0) {
496 fat.pop();
497 }
498 }
499 while fat.len() > sectors.num_sectors() as usize && fat.last() == Some(&consts::FREE_SECTOR)
504 || !validation.is_strict()
506 && fat.len() > sectors.num_sectors() as usize && fat.last() == Some(&consts::DIFAT_SECTOR)
507 {
508 fat.pop();
509 }
510 while fat.len() < sectors.num_sectors() as usize {
511 fat.push(consts::FREE_SECTOR);
512 }
513
514 let mut allocator =
515 Allocator::new(sectors, difat_sector_ids, difat, fat, validation)?;
516
517 let mut dir_entries = Vec::<DirEntry>::new();
519 let mut seen_dir_sectors = FnvHashSet::default();
520 let mut current_dir_sector = header.first_dir_sector;
521 let mut dir_sector_count = 1;
522 while current_dir_sector != consts::END_OF_CHAIN {
523 if validation.is_strict()
524 && header.version == Version::V4
525 && dir_sector_count > header.num_dir_sectors
526 {
527 invalid_data!(
528 "Directory chain includes at least {} sectors which is greater than header num_dir_sectors {}",
529 dir_sector_count,
530 header.num_dir_sectors
531 );
532 }
533 if current_dir_sector > consts::MAX_REGULAR_SECTOR {
534 invalid_data!(
535 "Directory chain includes invalid sector index {}",
536 current_dir_sector
537 );
538 } else if current_dir_sector >= num_sectors {
539 invalid_data!(
540 "Directory chain includes sector index {}, but sector \
541 count is only {}",
542 current_dir_sector,
543 num_sectors
544 );
545 }
546 if seen_dir_sectors.contains(¤t_dir_sector) {
547 invalid_data!(
548 "Directory chain includes duplicate sector index {}",
549 current_dir_sector,
550 );
551 }
552 seen_dir_sectors.insert(current_dir_sector);
553 {
554 let mut sector =
555 allocator.seek_to_sector(current_dir_sector)?;
556 for _ in 0..header.version.dir_entries_per_sector() {
557 dir_entries.push(DirEntry::read_from(
558 &mut sector,
559 header.version,
560 validation,
561 )?);
562 }
563 }
564 current_dir_sector = allocator.next(current_dir_sector)?;
565 dir_sector_count += 1;
566 }
567
568 let mut directory = Directory::new(
569 allocator,
570 dir_entries,
571 header.first_dir_sector,
572 validation,
573 )?;
574
575 let minifat = {
577 let mut chain = directory
578 .open_chain(header.first_minifat_sector, SectorInit::Fat)?;
579 if validation.is_strict()
580 && header.num_minifat_sectors as usize != chain.num_sectors()
581 {
582 invalid_data!(
583 "Incorrect MiniFAT chain length (header says {}, actual \
584 is {})",
585 header.num_minifat_sectors,
586 chain.num_sectors()
587 );
588 }
589 let num_minifat_entries = (chain.len() / 4) as usize;
590 let mut minifat = Vec::<u32>::with_capacity(num_minifat_entries);
591 for _ in 0..num_minifat_entries {
592 minifat.push(chain.read_le_u32()?);
593 }
594 while minifat.last() == Some(&consts::FREE_SECTOR) {
595 minifat.pop();
596 }
597 minifat
598 };
599
600 let minialloc = MiniAllocator::new(
601 directory,
602 minifat,
603 header.first_minifat_sector,
604 validation,
605 )?;
606
607 Ok(CompoundFile { minialloc: Arc::new(RwLock::new(minialloc)) })
608 }
609}
610
611impl<F: Read + Write + Seek> CompoundFile<F> {
612 pub fn create(inner: F) -> io::Result<CompoundFile<F>> {
615 CompoundFile::create_with_version(Version::V4, inner)
616 }
617
618 pub fn create_with_version(
621 version: Version,
622 mut inner: F,
623 ) -> io::Result<CompoundFile<F>> {
624 let mut header = Header {
625 version,
626 num_dir_sectors: if version == Version::V3 { 0 } else { 1 },
628 num_fat_sectors: 1,
629 first_dir_sector: 1,
630 first_minifat_sector: consts::END_OF_CHAIN,
631 num_minifat_sectors: 0,
632 first_difat_sector: consts::END_OF_CHAIN,
633 num_difat_sectors: 0,
634 initial_difat_entries: [consts::FREE_SECTOR;
635 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
636 };
637 header.initial_difat_entries[0] = 0;
638 header.write_to(&mut inner)?;
639
640 let sector_len = version.sector_len();
642 debug_assert!(sector_len >= consts::HEADER_LEN);
643 if sector_len > consts::HEADER_LEN {
644 inner.write_all(&vec![0; sector_len - consts::HEADER_LEN])?;
645 }
646
647 let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
649 for &entry in fat.iter() {
650 inner.write_le_u32(entry)?;
651 }
652 for _ in fat.len()..(sector_len / size_of::<u32>()) {
653 inner.write_le_u32(consts::FREE_SECTOR)?;
654 }
655 let difat: Vec<u32> = vec![0];
656 let difat_sector_ids: Vec<u32> = vec![];
657
658 let root_dir_entry = DirEntry::empty_root_entry();
660 root_dir_entry.write_to(&mut inner)?;
661 for _ in 1..version.dir_entries_per_sector() {
662 DirEntry::unallocated().write_to(&mut inner)?;
663 }
664
665 let sectors = Sectors::new(version, 3 * sector_len as u64, inner);
666 let allocator = Allocator::new(
667 sectors,
668 difat_sector_ids,
669 difat,
670 fat,
671 Validation::Strict,
672 )?;
673 let directory = Directory::new(
674 allocator,
675 vec![root_dir_entry],
676 1,
677 Validation::Strict,
678 )?;
679 let minialloc = MiniAllocator::new(
680 directory,
681 vec![],
682 consts::END_OF_CHAIN,
683 Validation::Strict,
684 )?;
685 Ok(CompoundFile { minialloc: Arc::new(RwLock::new(minialloc)) })
686 }
687
688 pub fn create_storage<P: AsRef<Path>>(
691 &mut self,
692 path: P,
693 ) -> io::Result<()> {
694 self.create_storage_with_path(path.as_ref())
695 }
696
697 fn create_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
698 let mut names = internal::path::name_chain_from_path(path)?;
699 if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
700 let path = internal::path::path_from_name_chain(&names);
701 if self.minialloc().dir_entry(stream_id).obj_type
702 != ObjType::Stream
703 {
704 already_exists!(
705 "Cannot create storage at {:?} because a \
706 storage already exists there",
707 path
708 );
709 } else {
710 already_exists!(
711 "Cannot create storage at {:?} because a \
712 stream already exists there",
713 path
714 );
715 }
716 }
717 debug_assert!(!names.is_empty());
720 let name = names.pop().unwrap();
721 let parent_id = match self.stream_id_for_name_chain(&names) {
722 Some(stream_id) => stream_id,
723 None => not_found!("Parent storage doesn't exist"),
724 };
725 self.minialloc_mut().insert_dir_entry(
726 parent_id,
727 name,
728 ObjType::Storage,
729 )?;
730 Ok(())
731 }
732
733 pub fn create_storage_all<P: AsRef<Path>>(
736 &mut self,
737 path: P,
738 ) -> io::Result<()> {
739 self.create_storage_all_with_path(path.as_ref())
740 }
741
742 fn create_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
743 let names = internal::path::name_chain_from_path(path)?;
744 for length in 1..(names.len() + 1) {
745 let prefix_path =
746 internal::path::path_from_name_chain(&names[..length]);
747 if self.is_storage(&prefix_path) {
748 continue;
749 }
750 self.create_storage_with_path(&prefix_path)?;
751 }
752 Ok(())
753 }
754
755 pub fn remove_storage<P: AsRef<Path>>(
758 &mut self,
759 path: P,
760 ) -> io::Result<()> {
761 self.remove_storage_with_path(path.as_ref())
762 }
763
764 fn remove_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
765 let mut names = internal::path::name_chain_from_path(path)?;
766 let stream_id = match self.stream_id_for_name_chain(&names) {
767 Some(parent_id) => parent_id,
768 None => not_found!("No such storage: {:?}", path),
769 };
770 {
771 let minialloc = self.minialloc();
772 let dir_entry = minialloc.dir_entry(stream_id);
773 if dir_entry.obj_type == ObjType::Root {
774 invalid_input!("Cannot remove the root storage object");
775 }
776 if dir_entry.obj_type == ObjType::Stream {
777 invalid_input!("Not a storage: {:?}", path);
778 }
779 debug_assert_eq!(dir_entry.obj_type, ObjType::Storage);
780 if dir_entry.child != consts::NO_STREAM {
781 invalid_input!("Storage is not empty: {:?}", path);
782 }
783 }
784 debug_assert!(!names.is_empty());
785 let name = names.pop().unwrap();
786 let parent_id = self.stream_id_for_name_chain(&names).unwrap();
787 self.minialloc_mut().remove_dir_entry(parent_id, name)?;
788 Ok(())
789 }
790
791 pub fn remove_storage_all<P: AsRef<Path>>(
795 &mut self,
796 path: P,
797 ) -> io::Result<()> {
798 self.remove_storage_all_with_path(path.as_ref())
799 }
800
801 fn remove_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
802 let mut stack = self.walk_storage(path)?.collect::<Vec<Entry>>();
803 while let Some(entry) = stack.pop() {
804 if entry.is_stream() {
805 self.remove_stream_with_path(entry.path())?;
806 } else if !entry.is_root() {
807 self.remove_storage_with_path(entry.path())?;
808 }
809 }
810 Ok(())
811 }
812
813 pub fn set_storage_clsid<P: AsRef<Path>>(
817 &mut self,
818 path: P,
819 clsid: Uuid,
820 ) -> io::Result<()> {
821 self.set_storage_clsid_with_path(path.as_ref(), clsid)
822 }
823
824 fn set_storage_clsid_with_path(
825 &mut self,
826 path: &Path,
827 clsid: Uuid,
828 ) -> io::Result<()> {
829 let names = internal::path::name_chain_from_path(path)?;
830 let stream_id = match self.stream_id_for_name_chain(&names) {
831 Some(stream_id) => stream_id,
832 None => not_found!(
833 "No such storage: {:?}",
834 internal::path::path_from_name_chain(&names)
835 ),
836 };
837 let mut minialloc = self.minialloc_mut();
838 if minialloc.dir_entry(stream_id).obj_type == ObjType::Stream {
839 invalid_input!(
840 "Not a storage: {:?}",
841 internal::path::path_from_name_chain(&names)
842 );
843 }
844 minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
845 dir_entry.clsid = clsid;
846 })
847 }
848
849 pub fn create_stream<P: AsRef<Path>>(
853 &mut self,
854 path: P,
855 ) -> io::Result<Stream<F>> {
856 self.create_stream_with_path(path.as_ref(), true)
857 }
858
859 pub fn create_new_stream<P: AsRef<Path>>(
863 &mut self,
864 path: P,
865 ) -> io::Result<Stream<F>> {
866 self.create_stream_with_path(path.as_ref(), false)
867 }
868
869 fn create_stream_with_path(
870 &mut self,
871 path: &Path,
872 overwrite: bool,
873 ) -> io::Result<Stream<F>> {
874 let mut names = internal::path::name_chain_from_path(path)?;
875 if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
876 if self.minialloc().dir_entry(stream_id).obj_type
877 != ObjType::Stream
878 {
879 already_exists!(
880 "Cannot create stream at {:?} because a \
881 storage already exists there",
882 internal::path::path_from_name_chain(&names)
883 );
884 } else if !overwrite {
885 already_exists!(
886 "Cannot create new stream at {:?} because a \
887 stream already exists there",
888 internal::path::path_from_name_chain(&names)
889 );
890 } else {
891 let mut stream = Stream::new(&self.minialloc, stream_id);
892 stream.set_len(0)?;
893 return Ok(stream);
894 }
895 }
896 debug_assert!(!names.is_empty());
899 let name = names.pop().unwrap();
900 let parent_id = match self.stream_id_for_name_chain(&names) {
901 Some(stream_id) => stream_id,
902 None => not_found!("Parent storage doesn't exist"),
903 };
904 let new_stream_id = self.minialloc_mut().insert_dir_entry(
905 parent_id,
906 name,
907 ObjType::Stream,
908 )?;
909 Ok(Stream::new(&self.minialloc, new_stream_id))
910 }
911
912 pub fn remove_stream<P: AsRef<Path>>(
914 &mut self,
915 path: P,
916 ) -> io::Result<()> {
917 self.remove_stream_with_path(path.as_ref())
918 }
919
920 fn remove_stream_with_path(&mut self, path: &Path) -> io::Result<()> {
921 let mut names = internal::path::name_chain_from_path(path)?;
922 let stream_id = match self.stream_id_for_name_chain(&names) {
923 Some(parent_id) => parent_id,
924 None => not_found!("No such stream: {:?}", path),
925 };
926 let (start_sector_id, is_in_mini_stream) = {
927 let minialloc = self.minialloc();
928 let dir_entry = minialloc.dir_entry(stream_id);
929 if dir_entry.obj_type != ObjType::Stream {
930 invalid_input!("Not a stream: {:?}", path);
931 }
932 debug_assert_eq!(dir_entry.child, consts::NO_STREAM);
933 (
934 dir_entry.start_sector,
935 dir_entry.stream_len < consts::MINI_STREAM_CUTOFF as u64,
936 )
937 };
938 if is_in_mini_stream {
939 self.minialloc_mut().free_mini_chain(start_sector_id)?;
940 } else {
941 self.minialloc_mut().free_chain(start_sector_id)?;
942 }
943 debug_assert!(!names.is_empty());
944 let name = names.pop().unwrap();
945 let parent_id = self.stream_id_for_name_chain(&names).unwrap();
946 self.minialloc_mut().remove_dir_entry(parent_id, name)?;
947 Ok(())
948 }
949
950 pub fn set_state_bits<P: AsRef<Path>>(
954 &mut self,
955 path: P,
956 bits: u32,
957 ) -> io::Result<()> {
958 self.set_entry_with_path(path.as_ref(), |dir_entry| {
959 dir_entry.state_bits = bits
960 })
961 }
962
963 pub fn touch<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
966 self.set_modified_time(path, std::time::SystemTime::now())
967 }
968
969 pub fn set_modified_time<P: AsRef<Path>>(
972 &mut self,
973 path: P,
974 ts: std::time::SystemTime,
975 ) -> io::Result<()> {
976 self.set_entry_with_path(path.as_ref(), |dir_entry| {
977 if dir_entry.obj_type != ObjType::Stream {
978 dir_entry.modified_time = Timestamp::from_system_time(ts);
979 }
980 })
981 }
982
983 pub fn set_created_time<P: AsRef<Path>>(
986 &mut self,
987 path: P,
988 ts: std::time::SystemTime,
989 ) -> io::Result<()> {
990 self.set_entry_with_path(path.as_ref(), |dir_entry| {
991 if dir_entry.obj_type != ObjType::Stream {
992 dir_entry.creation_time = Timestamp::from_system_time(ts);
993 }
994 })
995 }
996
997 fn set_entry_with_path<G: FnMut(&mut DirEntry)>(
998 &mut self,
999 path: &Path,
1000 f: G,
1001 ) -> io::Result<()> {
1002 let names = internal::path::name_chain_from_path(path)?;
1003 let path = internal::path::path_from_name_chain(&names);
1004 let stream_id = match self.stream_id_for_name_chain(&names) {
1005 Some(stream_id) => stream_id,
1006 None => not_found!("No such object: {:?}", path),
1007 };
1008 self.minialloc_mut().with_dir_entry_mut(stream_id, f)?;
1009 Ok(())
1010 }
1011
1012 pub fn flush(&mut self) -> io::Result<()> {
1014 self.minialloc_mut().flush()
1015 }
1016}
1017
1018impl<F: fmt::Debug> fmt::Debug for CompoundFile<F> {
1019 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1020 f.debug_tuple("CompoundFile").field(self.minialloc().inner()).finish()
1021 }
1022}
1023
1024trait ReadLeNumber: Read {
1025 fn read_le_u64(&mut self) -> Result<u64, std::io::Error> {
1026 let mut buf = [0u8; 8];
1027 self.read_exact(&mut buf)?;
1028 Ok(u64::from_le_bytes(buf))
1029 }
1030 fn read_le_u32(&mut self) -> Result<u32, std::io::Error> {
1031 let mut buf = [0u8; 4];
1032 self.read_exact(&mut buf)?;
1033 Ok(u32::from_le_bytes(buf))
1034 }
1035 fn read_le_u16(&mut self) -> Result<u16, std::io::Error> {
1036 let mut buf = [0u8; 2];
1037 self.read_exact(&mut buf)?;
1038 Ok(u16::from_le_bytes(buf))
1039 }
1040}
1041impl<T: Read> ReadLeNumber for T {}
1042
1043trait WriteLeNumber: Write {
1044 fn write_le_u64(&mut self, num: u64) -> Result<(), std::io::Error> {
1045 self.write_all(&num.to_le_bytes())
1046 }
1047 fn write_le_u32(&mut self, num: u32) -> Result<(), std::io::Error> {
1048 self.write_all(&num.to_le_bytes())
1049 }
1050 fn write_le_u16(&mut self, num: u16) -> Result<(), std::io::Error> {
1051 self.write_all(&num.to_le_bytes())
1052 }
1053}
1054impl<T: Write> WriteLeNumber for T {}
1055#[cfg(test)]
1058mod tests {
1059 use std::io::{self, Cursor, Seek, SeekFrom};
1060 use std::mem::size_of;
1061 use std::path::Path;
1062
1063 use crate::internal::{
1064 consts, DirEntry, Header, ObjType, Timestamp, Version,
1065 };
1066 use crate::{ReadLeNumber, WriteLeNumber};
1067
1068 use super::CompoundFile;
1069
1070 fn make_cfb_file_with_zero_padded_fat() -> io::Result<Vec<u8>> {
1071 let version = Version::V3;
1072 let mut data = Vec::<u8>::new();
1073 let mut header = Header {
1074 version,
1075 num_dir_sectors: 0,
1076 num_fat_sectors: 1,
1077 first_dir_sector: 1,
1078 first_minifat_sector: consts::END_OF_CHAIN,
1079 num_minifat_sectors: 0,
1080 first_difat_sector: consts::END_OF_CHAIN,
1081 num_difat_sectors: 0,
1082 initial_difat_entries: [consts::FREE_SECTOR;
1083 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
1084 };
1085 header.initial_difat_entries[0] = 0;
1086 header.write_to(&mut data)?;
1087 let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
1089 for &entry in fat.iter() {
1090 data.write_le_u32(entry)?;
1091 }
1092 for _ in fat.len()..(version.sector_len() / size_of::<u32>()) {
1096 data.write_le_u32(0)?;
1097 }
1098 DirEntry::empty_root_entry().write_to(&mut data)?;
1100 for _ in 1..version.dir_entries_per_sector() {
1101 DirEntry::unallocated().write_to(&mut data)?;
1102 }
1103 Ok(data)
1104 }
1105
1106 fn make_cfb_with_ts(ts: std::time::SystemTime) -> Vec<u8> {
1107 use std::io::Write;
1108
1109 let mut buf = Vec::new();
1110 let mut cfb = CompoundFile::create(io::Cursor::new(&mut buf)).unwrap();
1111
1112 cfb.create_storage("/foo/").unwrap();
1113 let mut stream = cfb.create_stream("/foo/bar").unwrap();
1114 stream.write_all(b"data").unwrap();
1115 drop(stream);
1116
1117 let entries: Vec<_> = cfb.walk().collect();
1118 for entr in entries {
1119 cfb.set_modified_time(entr.path(), ts).unwrap();
1120 cfb.set_created_time(entr.path(), ts).unwrap();
1121 }
1122 cfb.flush().unwrap();
1123 buf
1124 }
1125
1126 #[test]
1127 fn zero_padded_fat_strict() {
1128 let data = make_cfb_file_with_zero_padded_fat().unwrap();
1129 let result = CompoundFile::open_strict(Cursor::new(data));
1130 assert_eq!(
1131 result.err().unwrap().to_string(),
1132 "Malformed FAT (FAT has 128 entries, but file has only 2 sectors)"
1133 );
1134 }
1135
1136 #[test]
1138 fn zero_padded_fat_permissive() {
1139 let data = make_cfb_file_with_zero_padded_fat().unwrap();
1140 CompoundFile::open(Cursor::new(data)).expect("open");
1143 }
1144
1145 fn make_cfb_file_with_zero_padded_difat() -> io::Result<Vec<u8>> {
1146 let version = Version::V3;
1147 let mut data = Vec::<u8>::new();
1148
1149 let dir_sector = 0;
1150 let difat_sector = 1;
1151 let num_fat_sectors = consts::NUM_DIFAT_ENTRIES_IN_HEADER + 1;
1153 let first_fat_sector = difat_sector + 1;
1155 let fat_sectors: Vec<u32> = (0..num_fat_sectors)
1156 .map(|i| (first_fat_sector + i) as u32)
1157 .collect();
1158
1159 let header = Header {
1161 version,
1162 num_dir_sectors: 0,
1163 num_fat_sectors: num_fat_sectors as u32,
1164 first_dir_sector: dir_sector as u32,
1165 first_minifat_sector: consts::END_OF_CHAIN,
1166 num_minifat_sectors: 0,
1167 first_difat_sector: difat_sector as u32,
1168 num_difat_sectors: 1,
1169 initial_difat_entries: std::array::from_fn(|difat_entry_i| {
1170 fat_sectors[difat_entry_i]
1171 }),
1172 };
1173 header.write_to(&mut data)?;
1174
1175 DirEntry::empty_root_entry().write_to(&mut data)?;
1177 for _ in 1..version.dir_entries_per_sector() {
1178 DirEntry::unallocated().write_to(&mut data)?;
1179 }
1180
1181 let num_difat_entries_in_sector =
1183 version.sector_len() / size_of::<u32>() - 1;
1184 for i in 0..num_difat_entries_in_sector {
1185 let difat_entry_i = i + consts::NUM_DIFAT_ENTRIES_IN_HEADER;
1186
1187 let entry = if difat_entry_i < num_fat_sectors {
1188 fat_sectors[difat_entry_i]
1189 } else {
1190 0
1193 };
1194 data.write_le_u32(entry)?;
1195 }
1196 data.write_le_u32(consts::END_OF_CHAIN)?;
1198
1199 let num_fat_entries_in_sector =
1201 version.sector_len() / size_of::<u32>();
1202 let mut fat = vec![consts::FREE_SECTOR; num_fat_entries_in_sector * 2];
1203 fat[difat_sector] = consts::DIFAT_SECTOR;
1204 fat[dir_sector] = consts::END_OF_CHAIN;
1205 for fat_sector in fat_sectors {
1206 fat[fat_sector as usize] = consts::FAT_SECTOR;
1207 }
1208 for entry in fat {
1209 data.write_le_u32(entry)?;
1210 }
1211
1212 for _fat_sector in 2..num_fat_sectors {
1214 for _i in 0..num_fat_entries_in_sector {
1215 data.write_le_u32(consts::FREE_SECTOR)?;
1216 }
1217 }
1218
1219 Ok(data)
1220 }
1221
1222 #[test]
1223 fn zero_padded_difat_strict() {
1224 let data = make_cfb_file_with_zero_padded_difat().unwrap();
1225 let result = CompoundFile::open_strict(Cursor::new(data));
1226 assert_eq!(
1227 result.err().unwrap().to_string(),
1228 "Incorrect number of FAT sectors (header says 110, DIFAT says 236)",
1229 );
1230 }
1231
1232 #[test]
1234 fn zero_padded_difat_permissive() {
1235 let data = make_cfb_file_with_zero_padded_difat().unwrap();
1236 CompoundFile::open(Cursor::new(data)).expect("open");
1239 }
1240
1241 #[test]
1243 fn update_num_dir_sectors() {
1244 let cursor = Cursor::new(Vec::new());
1246 let mut comp = CompoundFile::create(cursor).unwrap();
1247 for i in 0..32 {
1250 let path = format!("stream{i}");
1251 let path = Path::new(&path);
1252 comp.create_stream(path).unwrap();
1253 }
1254 comp.flush().unwrap();
1255
1256 let mut cursor = comp.into_inner();
1258 cursor.seek(SeekFrom::Start(40)).unwrap();
1259 let num_dir_sectors = cursor.read_le_u32().unwrap();
1260 assert_eq!(num_dir_sectors, 2);
1261 }
1262
1263 #[test]
1264 fn deterministic_cfbs() {
1265 let ts = std::time::SystemTime::now();
1266 let cfb1 = make_cfb_with_ts(ts);
1267 let cfb2 = make_cfb_with_ts(ts);
1268 let ts = Timestamp::from_system_time(ts);
1269 assert_eq!(cfb1, cfb2);
1270
1271 let cfb = CompoundFile::open(Cursor::new(&cfb1)).unwrap();
1272
1273 let entry = cfb.entry("/foo").unwrap();
1274 assert_eq!(Timestamp::from_system_time(entry.created()), ts);
1275 assert_eq!(Timestamp::from_system_time(entry.modified()), ts);
1276
1277 let strict = CompoundFile::open_strict(Cursor::new(cfb1)).unwrap();
1278
1279 let entry = strict.entry("/foo").unwrap();
1280 assert_eq!(Timestamp::from_system_time(entry.created()), ts);
1281 assert_eq!(Timestamp::from_system_time(entry.modified()), ts);
1282 }
1283 fn make_cfb_with_inconsistent_difat_entries() -> io::Result<Vec<u8>> {
1284 let mut data = Vec::new();
1285 let mut hdr = Header {
1287 version: Version::V3,
1288 num_dir_sectors: 0,
1289 num_fat_sectors: 1,
1290 first_dir_sector: 0,
1291 first_minifat_sector: consts::END_OF_CHAIN,
1292 num_minifat_sectors: 0,
1293 first_difat_sector: consts::END_OF_CHAIN,
1294 num_difat_sectors: 0,
1295 initial_difat_entries: [consts::FREE_SECTOR;
1296 consts::NUM_DIFAT_ENTRIES_IN_HEADER],
1297 };
1298 hdr.initial_difat_entries[0] = 1;
1299
1300 hdr.write_to(&mut data)?;
1301
1302 for entr in [
1304 DirEntry::new("Root Entry", ObjType::Root, Timestamp::now()),
1305 DirEntry::unallocated(),
1306 DirEntry::unallocated(),
1307 DirEntry::unallocated(),
1308 ] {
1309 entr.write_to(&mut data)?;
1310 }
1311
1312 data.extend(&consts::END_OF_CHAIN.to_le_bytes());
1314 data.extend(&consts::FAT_SECTOR.to_le_bytes());
1315 data.extend(&consts::DIFAT_SECTOR.to_le_bytes());
1317 for _ in (0..128).skip(3) {
1318 data.extend(&consts::FREE_SECTOR.to_le_bytes());
1319 }
1320
1321 Ok(data)
1322 }
1323
1324 #[test]
1325 fn too_many_fat_entries() {
1326 use std::io::Write;
1327
1328 let cfb = make_cfb_with_inconsistent_difat_entries().unwrap();
1329
1330 let mut cfb = CompoundFile::open(Cursor::new(cfb)).unwrap();
1331 let mut f = cfb.create_stream("stream").unwrap();
1332 f.write_all(&vec![0; 1024 * 1024]).unwrap();
1333 }
1334}
1335
1336