chrome_cache_parser/
lib.rs

1//! A safe, zero-copy, rust-based chrome cache entry parser, supporting chrome cache versions 2.0,
2//! 2.1, and 3.0.
3pub mod block_file;
4pub mod cache_address;
5pub mod cache_index;
6pub mod error;
7pub mod time;
8
9pub use crate::cache_address::CacheAddr;
10use crate::cache_index::CacheVersion;
11pub use crate::cache_index::IndexHeader;
12pub use crate::error::{CCPError, CCPResult};
13
14use block_file::{DataFiles, LazyBlockFileCacheEntry, LazyBlockFileCacheEntryIterator};
15use cache_address::CACHE_ADDRESS_SIZE;
16use cache_index::INDEX_HEADER_SIZE;
17use std::cell::RefCell;
18use std::collections::HashMap;
19use std::fs;
20use std::io::Read;
21use std::path::{Path, PathBuf};
22use std::rc::Rc;
23use zerocopy::{FromBytes, Ref};
24
25/// A Chrome cache parser. Internally, it only stores the path to the cache and
26/// the cache's index as a buffer.
27pub struct ChromeCache {
28    path: PathBuf,
29    buffer: Vec<u8>,
30}
31
32impl ChromeCache {
33    pub fn from_path(path: PathBuf) -> CCPResult<ChromeCache> {
34        let index = Self::path_to_index(&path);
35        if !index.exists() {
36            return Err(CCPError::IndexDoesNotExist(
37                index.to_string_lossy().to_string(),
38            ));
39        }
40
41        let mut index_buffer = Vec::new();
42        let mut f = fs::File::open(index)?;
43        f.read_to_end(&mut index_buffer)?;
44
45        let chrome_cache = ChromeCache {
46            path,
47            buffer: index_buffer,
48        };
49
50        let header = ChromeCache::header(&chrome_cache)?;
51        let version = CacheVersion::from(header.version);
52
53        if header.magic != cache_index::INDEX_MAGIC {
54            return Err(CCPError::InvalidData("invalid index magic".to_string()));
55        }
56
57        if let CacheVersion::Unknown(version) = version {
58            return Err(CCPError::UnsupportedVersion(format!(
59                "unsupported version ({:x})",
60                version
61            )));
62        }
63
64        Ok(chrome_cache)
65    }
66
67    pub fn header(&self) -> CCPResult<&IndexHeader> {
68        IndexHeader::ref_from(&self.buffer[0..INDEX_HEADER_SIZE]).ok_or(CCPError::DataMisalignment(
69            "index header misalignment".to_string(),
70        ))
71    }
72
73    pub fn addresses(&self) -> CCPResult<&[CacheAddr]> {
74        let table_len = self.header()?.table_len as usize;
75        let begin = INDEX_HEADER_SIZE;
76        let end = begin + table_len * CACHE_ADDRESS_SIZE;
77        let addresses = Ref::<_, [CacheAddr]>::new_slice(&self.buffer[begin..end]).ok_or(
78            CCPError::DataMisalignment("cache address table misalignment".to_string()),
79        )?;
80
81        Ok(addresses.into_slice())
82    }
83
84    fn path_to_index(cache_dir: &Path) -> PathBuf {
85        cache_dir.join("index")
86    }
87
88    pub fn entries(&self) -> CCPResult<impl Iterator<Item = LazyBlockFileCacheEntry> + '_> {
89        // A map from the data file number to the data file.
90        let data_files = Rc::new(RefCell::new(DataFiles::new(
91            HashMap::new(),
92            self.path.to_path_buf(),
93        )));
94
95        let entries = self
96            .addresses()?
97            .iter()
98            .filter(|addr| addr.is_initialized())
99            .zip(std::iter::repeat(data_files))
100            .flat_map(|(addr, data_files)| {
101                LazyBlockFileCacheEntryIterator::new(data_files, *addr, self.path.to_path_buf())
102            });
103
104        Ok(entries)
105    }
106}