[go: up one dir, main page]

cfb/
lib.rs

1//! A library for reading/writing [Compound File Binary](
2//! https://en.wikipedia.org/wiki/Compound_File_Binary_Format) (structured
3//! storage) files.  See [MS-CFB](
4//! https://msdn.microsoft.com/en-us/library/dd942138.aspx) for the format
5//! specification.
6//!
7//! A Compound File Binary (CFB) file, also called a *structured storage file*
8//! or simply a *compound file*, is a bit like a simple file system within a
9//! file.  A compound file contains a tree of *storage* objects
10//! (i.e. directories), each of which can contain *stream* objects (i.e. files)
11//! or other storage objects.  The format is designed to allow reasonably
12//! efficient in-place mutation and resizing of these stream and storage
13//! objects, without having to completely rewrite the CFB file on disk.
14//!
15//! # Example usage
16//!
17//! ```no_run
18//! use cfb;
19//! use std::io::{Read, Seek, SeekFrom, Write};
20//!
21//! // Open an existing compound file in read-write mode.
22//! let mut comp = cfb::open_rw("path/to/cfb/file").unwrap();
23//!
24//! // Read in all the data from one of the streams in that compound file.
25//! let data = {
26//!     let mut stream = comp.open_stream("/foo/bar").unwrap();
27//!     let mut buffer = Vec::new();
28//!     stream.read_to_end(&mut buffer).unwrap();
29//!     buffer
30//! };
31//!
32//! // Append that data to the end of another stream in the same file.
33//! {
34//!     let mut stream = comp.open_stream("/baz").unwrap();
35//!     stream.seek(SeekFrom::End(0)).unwrap();
36//!     stream.write_all(&data).unwrap();
37//! }
38//!
39//! // Now create a new compound file, and create a new stream with the data.
40//! let mut comp2 = cfb::create("some/other/path").unwrap();
41//! comp2.create_storage("/spam/").unwrap();
42//! let mut stream = comp2.create_stream("/spam/eggs").unwrap();
43//! stream.write_all(&data).unwrap();
44//! ```
45
46#![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
68//===========================================================================//
69
70/// Opens an existing compound file at the given path in read-only mode.
71pub fn open<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
72    CompoundFile::open(fs::File::open(path)?)
73}
74
75/// Opens an existing compound file at the given path in read-write mode.
76pub 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
85/// Creates a new compound file with no contents at the given path.
86///
87/// The returned `CompoundFile` object will be both readable and writable.  If
88/// a file already exists at the given path, this will overwrite it.
89pub 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
103//===========================================================================//
104
105/// A compound file, backed by an underlying reader/writer (such as a
106/// [`File`](https://doc.rust-lang.org/std/fs/struct.File.html) or
107/// [`Cursor`](https://doc.rust-lang.org/std/io/struct.Cursor.html)).
108pub 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    /// Returns the CFB format version used for this compound file.
122    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    /// Returns information about the root storage object.  This is equivalent
131    /// to `self.entry("/").unwrap()` (but always succeeds).
132    pub fn root_entry(&self) -> Entry {
133        Entry::new(self.minialloc().root_dir_entry(), PathBuf::from("/"))
134    }
135
136    /// Given a path within the compound file, get information about that
137    /// stream or storage object.
138    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    /// Returns an iterator over the entries within the root storage object.
153    /// This is equivalent to `self.read_storage("/").unwrap()` (but always
154    /// succeeds).
155    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    /// Returns an iterator over the entries within a storage object.
166    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    /// Returns an iterator over all entries within the compound file, starting
204    /// from and including the root entry.  The iterator walks the storage tree
205    /// in a preorder traversal.  This is equivalent to
206    /// `self.walk_storage("/").unwrap()` (but always succeeds).
207    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    /// Returns an iterator over all entries under a storage subtree, including
217    /// the given path itself.  The iterator walks the storage tree in a
218    /// preorder traversal.
219    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    /// Returns true if there is an existing stream or storage at the given
249    /// path, or false if there is nothing at that path.
250    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    /// Returns true if there is an existing stream at the given path, or false
258    /// if there is a storage or nothing at that path.
259    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    /// Returns true if there is an existing storage at the given path, or
273    /// false if there is a stream or nothing at that path.
274    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    // TODO: pub fn copy_stream
288
289    // TODO: pub fn rename
290
291    /// Consumes the `CompoundFile`, returning the underlying reader/writer.
292    pub fn into_inner(self) -> F {
293        // We only ever retain Weak copies of the CompoundFile's minialloc Rc
294        // (e.g. in Stream structs), so the Rc::try_unwrap() should always
295        // succeed.
296        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    /// Opens an existing stream in the compound file for reading and/or
305    /// writing (depending on what the underlying file supports).
306    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    /// Opens an existing compound file, using the underlying reader.  If the
329    /// underlying reader also supports the `Write` trait, then the
330    /// `CompoundFile` object will be writable as well.
331    pub fn open(inner: F) -> io::Result<CompoundFile<F>> {
332        CompoundFile::open_internal(inner, Validation::Permissive)
333    }
334
335    /// Like `open()`, but is stricter when parsing and will return an error if
336    /// the file violates the CFB spec in any way (which many CFB files in the
337    /// wild do).  This is mainly useful for validating a CFB file or
338    /// implemention (such as this crate itself) to help ensure compatibility
339    /// with other readers.
340    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        // 2.2 Compound File Header
358        let header = Header::read_from(&mut inner, validation)?;
359        // Major Version
360        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        // Read in DIFAT.
381        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(&current_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        // The DIFAT should be padded with FREE_SECTOR, but DIFAT sectors
444        // may instead instead be incorrectly zero padded (see
445        // https://github.com/mdsteele/rust-cfb/issues/41).
446        // In case num_fat_sectors is not reliable, only remove zeroes,
447        // and don't remove sectors from the header DIFAT.
448        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        // Read in FAT.
471        let mut fat = Vec::<u32>::new();
472        for &sector_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 the number of sectors in the file is not a multiple of the number
486        // of FAT entries per sector, then the last FAT sector must be padded
487        // with FREE_SECTOR entries (see MS-CFB section 2.3).  However, some
488        // CFB implementations incorrectly pad the last FAT sector with zeros
489        // (see https://github.com/mdsteele/rust-cfb/issues/8), so we allow
490        // this under Permissive validation.  Since zero is normally a
491        // meaningful FAT entry (referring to sector 0), we only want to strip
492        // zeros from the end of the FAT if they are beyond the number of
493        // sectors in the file.
494        if !validation.is_strict() {
495            while fat.len() > num_sectors as usize && fat.last() == Some(&0) {
496                fat.pop();
497            }
498        }
499        // Strip FREE_SECTOR entries from the end of the FAT.  Unlike the zero
500        // case above, we can remove these even if it makes the number of FAT
501        // entries less than the number of sectors in the file; the allocator
502        // will implicitly treat these extra sectors as free.
503        while fat.len() > sectors.num_sectors() as usize && fat.last() == Some(&consts::FREE_SECTOR)
504            // strip DIFAT_SECTOR from the end
505            || !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        // Read in directory.
518        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(&current_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        // Read in MiniFAT.
576        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    /// Creates a new compound file with no contents, using the underlying
613    /// reader/writer.  The reader/writer should be initially empty.
614    pub fn create(inner: F) -> io::Result<CompoundFile<F>> {
615        CompoundFile::create_with_version(Version::V4, inner)
616    }
617
618    /// Creates a new compound file of the given version with no contents,
619    /// using the underlying writer.  The writer should be initially empty.
620    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            // 2.2 requires this to be zero in V3
627            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        // Pad the header with zeroes so it's the length of a sector.
641        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        // Write FAT sector:
648        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        // Write directory sector:
659        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    /// Creates a new, empty storage object (i.e. "directory") at the provided
689    /// path.  The parent storage object must already exist.
690    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        // If names is empty, that means we're trying to create the root.  But
718        // the root always already exists and will have been rejected above.
719        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    /// Recursively creates a storage and all of its parent storages if they
734    /// are missing.
735    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    /// Removes the storage object at the provided path.  The storage object
756    /// must exist and have no children.
757    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    /// Recursively removes a storage and all of its children.  If called on
792    /// the root storage, recursively removes all of its children but not the
793    /// root storage itself (which cannot be removed).
794    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    /// Sets the CLSID for the storage object at the provided path.  (To get
814    /// the current CLSID for a storage object, use
815    /// `self.entry(path)?.clsid()`.)
816    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    /// Creates and returns a new, empty stream object at the provided path.
850    /// If a stream already exists at that path, it will be replaced by the new
851    /// stream.  The parent storage object must already exist.
852    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    /// Creates and returns a new, empty stream object at the provided path.
860    /// Returns an error if a stream already exists at that path.  The parent
861    /// storage object must already exist.
862    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        // If names is empty, that means we're trying to create the root.  But
897        // the root always already exists and will have been rejected above.
898        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    /// Removes the stream object at the provided path.
913    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    /// Sets the user-defined bitflags for the object at the provided path.
951    /// (To get the current state bits for an object, use
952    /// `self.entry(path)?.state_bits()`.)
953    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    /// Sets the modified time for the object at the given path to now.  Has no
964    /// effect when called on the root storage.
965    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    /// Sets the modified time for the object at the given path.
970    /// Has no effect on streams due to requirements imposed by CFB spec.
971    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    /// Sets the created time for the object at the given path.
984    /// Has no effect on streams due to requirements imposed by CFB spec.
985    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    /// Flushes all changes to the underlying file.
1013    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//===========================================================================//
1056
1057#[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        // Write FAT sector:
1088        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        // Pad the FAT sector with zeros instead of FREE_SECTOR.  Technically
1093        // this violates the MS-CFB spec (section 2.3), but apparently some CFB
1094        // implementations do this.
1095        for _ in fat.len()..(version.sector_len() / size_of::<u32>()) {
1096            data.write_le_u32(0)?;
1097        }
1098        // Write directory sector:
1099        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    // Regression test for https://github.com/mdsteele/rust-cfb/issues/8.
1137    #[test]
1138    fn zero_padded_fat_permissive() {
1139        let data = make_cfb_file_with_zero_padded_fat().unwrap();
1140        // Despite the zero-padded FAT, we should be able to read this file
1141        // under Permissive validation.
1142        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        // The zero-padded DIFAT issue is only seen with a DIFAT sector
1152        let num_fat_sectors = consts::NUM_DIFAT_ENTRIES_IN_HEADER + 1;
1153        // Layout FAT sectors after the DIFAT sector
1154        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        // Construct header full of DIFAT entries
1160        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        // Write the directory sector
1176        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        // Write the DIFAT sector
1182        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                // Pad with zeroes instead of FREE_SECTOR, this is
1191                // the point where it deviates from spec.
1192                0
1193            };
1194            data.write_le_u32(entry)?;
1195        }
1196        // End DIFAT chain
1197        data.write_le_u32(consts::END_OF_CHAIN)?;
1198
1199        // Write the first two FAT sectors, referencing the header data
1200        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        // Pad out the rest of the FAT sectors with FREE_SECTOR
1213        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    // Regression test for https://github.com/mdsteele/rust-cfb/issues/41.
1233    #[test]
1234    fn zero_padded_difat_permissive() {
1235        let data = make_cfb_file_with_zero_padded_difat().unwrap();
1236        // Despite the zero-padded DIFAT, we should be able to read this file
1237        // under Permissive validation.
1238        CompoundFile::open(Cursor::new(data)).expect("open");
1239    }
1240
1241    // Regression test for https://github.com/mdsteele/rust-cfb/issues/52.
1242    #[test]
1243    fn update_num_dir_sectors() {
1244        // Create a CFB file with 2 sectors for the directory.
1245        let cursor = Cursor::new(Vec::new());
1246        let mut comp = CompoundFile::create(cursor).unwrap();
1247        // root + 31 entries in the first sector
1248        // 1 stream entry in the second sector
1249        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        // read num_dir_sectors from the header
1257        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        // cfb has spare DIFAT_SECTOR entries in FAT not accounted for in header
1286        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        // write dir sector
1303        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        // write FAT sector
1313        data.extend(&consts::END_OF_CHAIN.to_le_bytes());
1314        data.extend(&consts::FAT_SECTOR.to_le_bytes());
1315        // add a DIFAT_SECTOR to FAT, although inconsistent with header
1316        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//===========================================================================//