#![warn(missing_docs)]
use crate::internal::consts::{self, NO_STREAM};
use crate::internal::{
Allocator, DirEntry, Directory, Header, MiniAllocator, ObjType,
SectorInit, Sectors,
};
pub use crate::internal::{Entries, Entry, Version};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fnv::FnvHashSet;
use std::fs;
use std::io::{self, BufRead, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use std::path::{Path, PathBuf};
use uuid::Uuid;
#[macro_use]
mod internal;
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
CompoundFile::open(fs::File::open(path)?)
}
pub fn open_rw<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
open_rw_with_path(path.as_ref())
}
fn open_rw_with_path(path: &Path) -> io::Result<CompoundFile<fs::File>> {
let file = fs::OpenOptions::new().read(true).write(true).open(path)?;
CompoundFile::open(file)
}
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<CompoundFile<fs::File>> {
create_with_path(path.as_ref())
}
fn create_with_path(path: &Path) -> io::Result<CompoundFile<fs::File>> {
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)?;
CompoundFile::create(file)
}
pub struct CompoundFile<F> {
minialloc: MiniAllocator<F>,
}
impl<F> CompoundFile<F> {
pub fn version(&self) -> Version {
self.minialloc.version()
}
fn stream_id_for_name_chain(&self, names: &[&str]) -> Option<u32> {
self.minialloc.stream_id_for_name_chain(names)
}
pub fn root_entry(&self) -> Entry {
Entry::new(self.minialloc.root_dir_entry(), PathBuf::from("/"))
}
pub fn entry<P: AsRef<Path>>(&self, path: P) -> io::Result<Entry> {
self.entry_with_path(path.as_ref())
}
fn entry_with_path(&self, path: &Path) -> io::Result<Entry> {
let names = internal::path::name_chain_from_path(path)?;
let path = internal::path::path_from_name_chain(&names);
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => not_found!("No such object: {:?}", path),
};
Ok(Entry::new(self.minialloc.dir_entry(stream_id), path))
}
pub fn read_root_storage(&self) -> Entries {
self.minialloc.root_storage_entries()
}
pub fn read_storage<P: AsRef<Path>>(
&self,
path: P,
) -> io::Result<Entries> {
self.minialloc.storage_entries(path.as_ref())
}
pub fn walk(&self) -> Entries {
self.minialloc.walk()
}
pub fn walk_storage<P: AsRef<Path>>(
&self,
path: P,
) -> io::Result<Entries> {
self.minialloc.walk_storage(path.as_ref())
}
pub fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
match internal::path::name_chain_from_path(path.as_ref()) {
Ok(names) => self.stream_id_for_name_chain(&names).is_some(),
Err(_) => false,
}
}
pub fn is_stream<P: AsRef<Path>>(&self, path: P) -> bool {
match internal::path::name_chain_from_path(path.as_ref()) {
Ok(names) => match self.stream_id_for_name_chain(&names) {
Some(stream_id) => {
self.minialloc.dir_entry(stream_id).obj_type
== ObjType::Stream
}
None => false,
},
Err(_) => false,
}
}
pub fn is_storage<P: AsRef<Path>>(&self, path: P) -> bool {
match internal::path::name_chain_from_path(path.as_ref()) {
Ok(names) => match self.stream_id_for_name_chain(&names) {
Some(stream_id) => {
self.minialloc.dir_entry(stream_id).obj_type
!= ObjType::Stream
}
None => false,
},
Err(_) => false,
}
}
pub fn into_inner(self) -> F {
self.minialloc.into_inner()
}
}
impl<F: Seek> CompoundFile<F> {
pub fn open_stream<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<Stream<F>> {
self.open_stream_with_path(path.as_ref())
}
fn open_stream_with_path(&mut self, path: &Path) -> io::Result<Stream<F>> {
let names = internal::path::name_chain_from_path(path)?;
let path = internal::path::path_from_name_chain(&names);
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => not_found!("No such stream: {:?}", path),
};
if self.minialloc.dir_entry(stream_id).obj_type != ObjType::Stream {
invalid_input!("Not a stream: {:?}", path);
}
Ok(Stream::new(self, stream_id))
}
}
impl<F: Read + Seek> CompoundFile<F> {
pub fn open(mut inner: F) -> io::Result<CompoundFile<F>> {
let inner_len = inner.seek(SeekFrom::End(0))?;
if inner_len < consts::HEADER_LEN as u64 {
invalid_data!(
"Invalid CFB file ({} bytes is too small)",
inner_len
);
}
inner.seek(SeekFrom::Start(0))?;
let header = Header::read_from(&mut inner)?;
let sector_len = header.version.sector_len();
if inner_len
> ((consts::MAX_REGULAR_SECTOR + 1) as u64) * (sector_len as u64)
{
invalid_data!(
"Invalid CFB file ({} bytes is too large)",
inner_len
);
}
if inner_len < header.version.sector_len() as u64 {
invalid_data!(
"Invalid CFB file (length of {} < sector length of {})",
inner_len,
header.version.sector_len()
);
}
let mut sectors = Sectors::new(header.version, inner_len, inner);
let num_sectors = sectors.num_sectors();
let mut difat = Vec::<u32>::new();
difat.extend_from_slice(&header.initial_difat_entries);
let mut seen_sector_ids = FnvHashSet::default();
let mut difat_sector_ids = Vec::new();
let mut current_difat_sector = header.first_difat_sector;
while current_difat_sector != consts::END_OF_CHAIN {
if current_difat_sector > consts::MAX_REGULAR_SECTOR {
invalid_data!(
"DIFAT chain includes invalid sector index {}",
current_difat_sector
);
} else if current_difat_sector >= num_sectors {
invalid_data!(
"DIFAT chain includes sector index {}, but sector count \
is only {}",
current_difat_sector,
num_sectors
);
}
if seen_sector_ids.contains(¤t_difat_sector) {
invalid_data!(
"DIFAT chain includes duplicate sector index {}",
current_difat_sector,
);
}
seen_sector_ids.insert(current_difat_sector);
difat_sector_ids.push(current_difat_sector);
let mut sector = sectors.seek_to_sector(current_difat_sector)?;
for _ in 0..(sector_len / size_of::<u32>() - 1) {
let next = sector.read_u32::<LittleEndian>()?;
if next != consts::FREE_SECTOR
&& next > consts::MAX_REGULAR_SECTOR
{
invalid_data!(
"DIFAT refers to invalid sector index {}",
next
);
}
difat.push(next);
}
current_difat_sector = sector.read_u32::<LittleEndian>()?;
}
if header.num_difat_sectors as usize != difat_sector_ids.len() {
invalid_data!(
"Incorrect DIFAT chain length (header says {}, actual is {})",
header.num_difat_sectors,
difat_sector_ids.len()
);
}
while difat.last() == Some(&consts::FREE_SECTOR) {
difat.pop();
}
if header.num_fat_sectors as usize != difat.len() {
invalid_data!(
"Incorrect number of FAT sectors (header says {}, DIFAT says \
{})",
header.num_fat_sectors,
difat.len()
);
}
let mut fat = Vec::<u32>::new();
for §or_index in difat.iter() {
if sector_index >= num_sectors {
invalid_data!(
"DIFAT refers to sector {}, but sector count is only {}",
sector_index,
num_sectors
);
}
let mut sector = sectors.seek_to_sector(sector_index)?;
for _ in 0..(sector_len / size_of::<u32>()) {
fat.push(sector.read_u32::<LittleEndian>()?);
}
}
while fat.len() > num_sectors as usize && fat.last() == Some(&0) {
fat.pop();
}
while fat.last() == Some(&consts::FREE_SECTOR) {
fat.pop();
}
let mut allocator =
Allocator::new(sectors, difat_sector_ids, difat, fat)?;
let mut dir_entries = Vec::<DirEntry>::new();
let mut seen_dir_sectors = FnvHashSet::default();
let mut current_dir_sector = header.first_dir_sector;
while current_dir_sector != consts::END_OF_CHAIN {
if current_dir_sector > consts::MAX_REGULAR_SECTOR {
invalid_data!(
"Directory chain includes invalid sector index {}",
current_dir_sector
);
} else if current_dir_sector >= num_sectors {
invalid_data!(
"Directory chain includes sector index {}, but sector \
count is only {}",
current_dir_sector,
num_sectors
);
}
if seen_dir_sectors.contains(¤t_dir_sector) {
invalid_data!(
"Directory chain includes duplicate sector index {}",
current_dir_sector,
);
}
seen_dir_sectors.insert(current_dir_sector);
{
let mut sector =
allocator.seek_to_sector(current_dir_sector)?;
for _ in 0..header.version.dir_entries_per_sector() {
dir_entries.push(DirEntry::read_from(
&mut sector,
header.version,
)?);
}
}
current_dir_sector = allocator.next(current_dir_sector)?;
}
let mut directory =
Directory::new(allocator, dir_entries, header.first_dir_sector)?;
let minifat = {
let mut chain = directory
.open_chain(header.first_minifat_sector, SectorInit::Fat)?;
if header.num_minifat_sectors as usize != chain.num_sectors() {
invalid_data!(
"Incorrect MiniFAT chain length (header says {}, actual \
is {})",
header.num_minifat_sectors,
chain.num_sectors()
);
}
let num_minifat_entries = (chain.len() / 4) as usize;
let mut minifat = Vec::<u32>::with_capacity(num_minifat_entries);
for _ in 0..num_minifat_entries {
minifat.push(chain.read_u32::<LittleEndian>()?);
}
while minifat.last() == Some(&consts::FREE_SECTOR) {
minifat.pop();
}
minifat
};
let minialloc = MiniAllocator::new(
directory,
minifat,
header.first_minifat_sector,
)?;
Ok(CompoundFile { minialloc })
}
fn read_data_from_stream(
&mut self,
stream_id: u32,
buf_offset_from_start: u64,
buf: &mut [u8],
) -> io::Result<usize> {
let (start_sector, stream_len) = {
let dir_entry = self.minialloc.dir_entry(stream_id);
debug_assert_eq!(dir_entry.obj_type, ObjType::Stream);
(dir_entry.start_sector, dir_entry.stream_len)
};
let num_bytes = if buf_offset_from_start >= stream_len {
0
} else {
let remaining = stream_len - buf_offset_from_start;
if remaining < buf.len() as u64 {
remaining as usize
} else {
buf.len()
}
};
if num_bytes > 0 {
if stream_len < consts::MINI_STREAM_CUTOFF as u64 {
let mut chain =
self.minialloc.open_mini_chain(start_sector)?;
chain.seek(SeekFrom::Start(buf_offset_from_start))?;
chain.read_exact(&mut buf[..num_bytes])?;
} else {
let mut chain = self
.minialloc
.open_chain(start_sector, SectorInit::Zero)?;
chain.seek(SeekFrom::Start(buf_offset_from_start))?;
chain.read_exact(&mut buf[..num_bytes])?;
}
}
Ok(num_bytes)
}
}
impl<F: Read + Write + Seek> CompoundFile<F> {
pub fn create(inner: F) -> io::Result<CompoundFile<F>> {
CompoundFile::create_with_version(Version::V4, inner)
}
pub fn create_with_version(
version: Version,
mut inner: F,
) -> io::Result<CompoundFile<F>> {
let mut header = Header {
version,
num_dir_sectors: 1,
num_fat_sectors: 1,
first_dir_sector: 1,
first_minifat_sector: consts::END_OF_CHAIN,
num_minifat_sectors: 0,
first_difat_sector: consts::END_OF_CHAIN,
num_difat_sectors: 0,
initial_difat_entries: [consts::FREE_SECTOR;
consts::NUM_DIFAT_ENTRIES_IN_HEADER],
};
header.initial_difat_entries[0] = 0;
header.write_to(&mut inner)?;
let sector_len = version.sector_len();
debug_assert!(sector_len >= consts::HEADER_LEN);
if sector_len > consts::HEADER_LEN {
inner.write_all(&vec![0; sector_len - consts::HEADER_LEN])?;
}
let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
for &entry in fat.iter() {
inner.write_u32::<LittleEndian>(entry)?;
}
for _ in fat.len()..(sector_len / size_of::<u32>()) {
inner.write_u32::<LittleEndian>(consts::FREE_SECTOR)?;
}
let difat: Vec<u32> = vec![0];
let difat_sector_ids: Vec<u32> = vec![];
let root_dir_entry = DirEntry::empty_root_entry();
root_dir_entry.write_to(&mut inner)?;
for _ in 1..version.dir_entries_per_sector() {
DirEntry::unallocated().write_to(&mut inner)?;
}
let sectors = Sectors::new(version, 3 * sector_len as u64, inner);
let allocator = Allocator::new(sectors, difat_sector_ids, difat, fat)
.expect("allocator");
let directory = Directory::new(allocator, vec![root_dir_entry], 1)
.expect("directory");
let minialloc =
MiniAllocator::new(directory, vec![], consts::END_OF_CHAIN)
.expect("minialloc");
Ok(CompoundFile { minialloc })
}
pub fn create_storage<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<()> {
self.create_storage_with_path(path.as_ref())
}
fn create_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
let mut names = internal::path::name_chain_from_path(path)?;
if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
let path = internal::path::path_from_name_chain(&names);
if self.minialloc.dir_entry(stream_id).obj_type != ObjType::Stream
{
already_exists!(
"Cannot create storage at {:?} because a \
storage already exists there",
path
);
} else {
already_exists!(
"Cannot create storage at {:?} because a \
stream already exists there",
path
);
}
}
debug_assert!(!names.is_empty());
let name = names.pop().unwrap();
let parent_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => {
not_found!("Parent storage doesn't exist");
}
};
self.minialloc.insert_dir_entry(parent_id, name, ObjType::Storage)?;
Ok(())
}
pub fn create_storage_all<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<()> {
self.create_storage_all_with_path(path.as_ref())
}
fn create_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
let names = internal::path::name_chain_from_path(path)?;
for length in 1..(names.len() + 1) {
let prefix_path =
internal::path::path_from_name_chain(&names[..length]);
if self.is_storage(&prefix_path) {
continue;
}
self.create_storage_with_path(&prefix_path)?;
}
Ok(())
}
pub fn remove_storage<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<()> {
self.remove_storage_with_path(path.as_ref())
}
fn remove_storage_with_path(&mut self, path: &Path) -> io::Result<()> {
let mut names = internal::path::name_chain_from_path(path)?;
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(parent_id) => parent_id,
None => not_found!("No such storage: {:?}", path),
};
{
let dir_entry = self.minialloc.dir_entry(stream_id);
if dir_entry.obj_type == ObjType::Root {
invalid_input!("Cannot remove the root storage object");
}
if dir_entry.obj_type == ObjType::Stream {
invalid_input!("Not a storage: {:?}", path);
}
debug_assert_eq!(dir_entry.obj_type, ObjType::Storage);
if dir_entry.child != NO_STREAM {
invalid_input!("Storage is not empty: {:?}", path);
}
}
debug_assert!(!names.is_empty());
let name = names.pop().unwrap();
let parent_id = self.stream_id_for_name_chain(&names).unwrap();
self.minialloc.remove_dir_entry(parent_id, name)?;
Ok(())
}
pub fn remove_storage_all<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<()> {
self.remove_storage_all_with_path(path.as_ref())
}
fn remove_storage_all_with_path(&mut self, path: &Path) -> io::Result<()> {
let mut stack = Vec::<Entry>::new();
for entry in self.minialloc.walk_storage(path)? {
stack.push(entry);
}
while let Some(entry) = stack.pop() {
if entry.is_stream() {
self.remove_stream_with_path(entry.path())?;
} else if !entry.is_root() {
self.remove_storage_with_path(entry.path())?;
}
}
Ok(())
}
pub fn set_storage_clsid<P: AsRef<Path>>(
&mut self,
path: P,
clsid: Uuid,
) -> io::Result<()> {
self.set_storage_clsid_with_path(path.as_ref(), clsid)
}
fn set_storage_clsid_with_path(
&mut self,
path: &Path,
clsid: Uuid,
) -> io::Result<()> {
let names = internal::path::name_chain_from_path(path)?;
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => not_found!(
"No such storage: {:?}",
internal::path::path_from_name_chain(&names)
),
};
if self.minialloc.dir_entry(stream_id).obj_type == ObjType::Stream {
invalid_input!(
"Not a storage: {:?}",
internal::path::path_from_name_chain(&names)
);
}
self.minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
dir_entry.clsid = clsid;
})
}
pub fn create_stream<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<Stream<F>> {
self.create_stream_with_path(path.as_ref(), true)
}
pub fn create_new_stream<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<Stream<F>> {
self.create_stream_with_path(path.as_ref(), false)
}
fn create_stream_with_path(
&mut self,
path: &Path,
overwrite: bool,
) -> io::Result<Stream<F>> {
let mut names = internal::path::name_chain_from_path(path)?;
if let Some(stream_id) = self.stream_id_for_name_chain(&names) {
if self.minialloc.dir_entry(stream_id).obj_type != ObjType::Stream
{
already_exists!(
"Cannot create stream at {:?} because a \
storage already exists there",
internal::path::path_from_name_chain(&names)
);
} else if !overwrite {
already_exists!(
"Cannot create new stream at {:?} because a \
stream already exists there",
internal::path::path_from_name_chain(&names)
);
} else {
let mut stream = Stream::new(self, stream_id);
stream.set_len(0)?;
return Ok(stream);
}
}
debug_assert!(!names.is_empty());
let name = names.pop().unwrap();
let parent_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => {
not_found!("Parent storage doesn't exist");
}
};
let new_stream_id = self.minialloc.insert_dir_entry(
parent_id,
name,
ObjType::Stream,
)?;
return Ok(Stream::new(self, new_stream_id));
}
pub fn remove_stream<P: AsRef<Path>>(
&mut self,
path: P,
) -> io::Result<()> {
self.remove_stream_with_path(path.as_ref())
}
fn remove_stream_with_path(&mut self, path: &Path) -> io::Result<()> {
let mut names = internal::path::name_chain_from_path(path)?;
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(parent_id) => parent_id,
None => not_found!("No such stream: {:?}", path),
};
let (start_sector_id, is_in_mini_stream) = {
let dir_entry = self.minialloc.dir_entry(stream_id);
if dir_entry.obj_type != ObjType::Stream {
invalid_input!("Not a stream: {:?}", path);
}
debug_assert_eq!(dir_entry.child, NO_STREAM);
(
dir_entry.start_sector,
dir_entry.stream_len < consts::MINI_STREAM_CUTOFF as u64,
)
};
if is_in_mini_stream {
self.minialloc.free_mini_chain(start_sector_id)?;
} else {
self.minialloc.free_chain(start_sector_id)?;
}
debug_assert!(!names.is_empty());
let name = names.pop().unwrap();
let parent_id = self.stream_id_for_name_chain(&names).unwrap();
self.minialloc.remove_dir_entry(parent_id, name)?;
Ok(())
}
pub fn set_state_bits<P: AsRef<Path>>(
&mut self,
path: P,
bits: u32,
) -> io::Result<()> {
self.set_state_bits_with_path(path.as_ref(), bits)
}
fn set_state_bits_with_path(
&mut self,
path: &Path,
bits: u32,
) -> io::Result<()> {
let names = internal::path::name_chain_from_path(path)?;
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => not_found!(
"No such object: {:?}",
internal::path::path_from_name_chain(&names)
),
};
self.minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
dir_entry.state_bits = bits;
})
}
pub fn touch<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
self.touch_with_path(path.as_ref())
}
fn touch_with_path(&mut self, path: &Path) -> io::Result<()> {
let names = internal::path::name_chain_from_path(path)?;
let path = internal::path::path_from_name_chain(&names);
let stream_id = match self.stream_id_for_name_chain(&names) {
Some(stream_id) => stream_id,
None => not_found!("No such object: {:?}", path),
};
if stream_id != consts::ROOT_STREAM_ID {
debug_assert_ne!(
self.minialloc.dir_entry(stream_id).obj_type,
ObjType::Root
);
self.minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
dir_entry.modified_time = internal::time::current_timestamp();
})?;
}
Ok(())
}
pub fn flush(&mut self) -> io::Result<()> {
self.minialloc.flush()
}
fn write_data_to_stream(
&mut self,
stream_id: u32,
buf_offset_from_start: u64,
buf: &[u8],
) -> io::Result<()> {
let (old_start_sector, old_stream_len) = {
let dir_entry = self.minialloc.dir_entry(stream_id);
debug_assert_eq!(dir_entry.obj_type, ObjType::Stream);
(dir_entry.start_sector, dir_entry.stream_len)
};
debug_assert!(buf_offset_from_start <= old_stream_len);
let new_stream_len =
old_stream_len.max(buf_offset_from_start + buf.len() as u64);
let new_start_sector = if old_start_sector == consts::END_OF_CHAIN {
debug_assert_eq!(old_stream_len, 0);
debug_assert_eq!(buf_offset_from_start, 0);
if new_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
let mut chain =
self.minialloc.open_mini_chain(consts::END_OF_CHAIN)?;
chain.write_all(buf)?;
chain.start_sector_id()
} else {
let mut chain = self
.minialloc
.open_chain(consts::END_OF_CHAIN, SectorInit::Zero)?;
chain.write_all(buf)?;
chain.start_sector_id()
}
} else if old_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
if new_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
let mut chain =
self.minialloc.open_mini_chain(old_start_sector)?;
chain.seek(SeekFrom::Start(buf_offset_from_start))?;
chain.write_all(buf)?;
debug_assert_eq!(chain.start_sector_id(), old_start_sector);
old_start_sector
} else {
debug_assert!(
buf_offset_from_start < consts::MINI_STREAM_CUTOFF as u64
);
let mut tmp = vec![0u8; buf_offset_from_start as usize];
let mut chain =
self.minialloc.open_mini_chain(old_start_sector)?;
chain.read_exact(&mut tmp)?;
chain.free()?;
let mut chain = self
.minialloc
.open_chain(consts::END_OF_CHAIN, SectorInit::Zero)?;
chain.write_all(&tmp)?;
chain.write_all(buf)?;
chain.start_sector_id()
}
} else {
debug_assert!(new_stream_len >= consts::MINI_STREAM_CUTOFF as u64);
let mut chain = self
.minialloc
.open_chain(old_start_sector, SectorInit::Zero)?;
chain.seek(SeekFrom::Start(buf_offset_from_start))?;
chain.write_all(buf)?;
debug_assert_eq!(chain.start_sector_id(), old_start_sector);
old_start_sector
};
self.minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
dir_entry.start_sector = new_start_sector;
dir_entry.stream_len = new_stream_len;
dir_entry.modified_time = internal::time::current_timestamp();
})
}
fn resize_stream(
&mut self,
stream_id: u32,
new_stream_len: u64,
) -> io::Result<()> {
let (old_start_sector, old_stream_len) = {
let dir_entry = self.minialloc.dir_entry(stream_id);
debug_assert_eq!(dir_entry.obj_type, ObjType::Stream);
(dir_entry.start_sector, dir_entry.stream_len)
};
let new_start_sector = if old_start_sector == consts::END_OF_CHAIN {
debug_assert_eq!(old_stream_len, 0);
if new_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
let mut chain =
self.minialloc.open_mini_chain(consts::END_OF_CHAIN)?;
chain.set_len(new_stream_len)?;
chain.start_sector_id()
} else {
let mut chain = self
.minialloc
.open_chain(consts::END_OF_CHAIN, SectorInit::Zero)?;
chain.set_len(new_stream_len)?;
chain.start_sector_id()
}
} else if old_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
if new_stream_len == 0 {
self.minialloc.free_mini_chain(old_start_sector)?;
consts::END_OF_CHAIN
} else if new_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
let mut chain =
self.minialloc.open_mini_chain(old_start_sector)?;
chain.set_len(new_stream_len)?;
debug_assert_eq!(chain.start_sector_id(), old_start_sector);
old_start_sector
} else {
let mut tmp = vec![0u8; old_stream_len as usize];
let mut chain =
self.minialloc.open_mini_chain(old_start_sector)?;
chain.read_exact(&mut tmp)?;
chain.free()?;
let mut chain = self
.minialloc
.open_chain(consts::END_OF_CHAIN, SectorInit::Zero)?;
chain.write_all(&tmp)?;
chain.set_len(new_stream_len)?;
chain.start_sector_id()
}
} else {
if new_stream_len == 0 {
self.minialloc.free_chain(old_start_sector)?;
consts::END_OF_CHAIN
} else if new_stream_len < consts::MINI_STREAM_CUTOFF as u64 {
debug_assert!(new_stream_len < old_stream_len);
let mut tmp = vec![0u8; new_stream_len as usize];
let mut chain = self
.minialloc
.open_chain(old_start_sector, SectorInit::Zero)?;
chain.read_exact(&mut tmp)?;
chain.free()?;
let mut chain =
self.minialloc.open_mini_chain(consts::END_OF_CHAIN)?;
chain.write_all(&tmp)?;
chain.start_sector_id()
} else {
let mut chain = self
.minialloc
.open_chain(old_start_sector, SectorInit::Zero)?;
chain.set_len(new_stream_len)?;
debug_assert_eq!(chain.start_sector_id(), old_start_sector);
old_start_sector
}
};
self.minialloc.with_dir_entry_mut(stream_id, |dir_entry| {
dir_entry.start_sector = new_start_sector;
dir_entry.stream_len = new_stream_len;
dir_entry.modified_time = internal::time::current_timestamp();
})
}
}
const BUFFER_SIZE: usize = 8192;
pub struct Stream<'a, F: 'a> {
comp: &'a mut CompoundFile<F>,
stream_id: u32,
total_len: u64,
buffer: Box<[u8; BUFFER_SIZE]>,
buf_pos: usize,
buf_cap: usize,
buf_offset_from_start: u64,
flusher: Option<Box<dyn Flusher<F>>>,
}
impl<'a, F> Stream<'a, F> {
pub(crate) fn new(
comp: &'a mut CompoundFile<F>,
stream_id: u32,
) -> Stream<'a, F> {
let total_len = comp.minialloc.dir_entry(stream_id).stream_len;
Stream {
comp,
stream_id,
total_len,
buffer: Box::new([0; BUFFER_SIZE]),
buf_pos: 0,
buf_cap: 0,
buf_offset_from_start: 0,
flusher: None,
}
}
pub fn len(&self) -> u64 {
self.total_len
}
pub fn is_empty(&self) -> bool {
self.total_len == 0
}
fn current_position(&self) -> u64 {
self.buf_offset_from_start + (self.buf_pos as u64)
}
fn flush_changes(&mut self) -> io::Result<()> {
if let Some(flusher) = self.flusher.take() {
flusher.flush_changes(self)?;
}
Ok(())
}
}
impl<'a, F: Read + Write + Seek> Stream<'a, F> {
pub fn set_len(&mut self, size: u64) -> io::Result<()> {
if size != self.total_len {
let new_position = self.current_position().min(size);
self.flush_changes()?;
self.comp.resize_stream(self.stream_id, size)?;
self.total_len = size;
self.buf_offset_from_start = new_position;
self.buf_pos = 0;
self.buf_cap = 0;
}
Ok(())
}
fn mark_modified(&mut self) {
if self.flusher.is_none() {
let flusher: Box<dyn Flusher<F>> = Box::new(FlushBuffer);
self.flusher = Some(flusher);
}
}
}
impl<'a, F: Read + Seek> BufRead for Stream<'a, F> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.buf_pos >= self.buf_cap
&& self.current_position() < self.total_len
{
self.flush_changes()?;
self.buf_offset_from_start += self.buf_pos as u64;
self.buf_pos = 0;
self.buf_cap = self.comp.read_data_from_stream(
self.stream_id,
self.buf_offset_from_start,
&mut self.buffer[..],
)?;
}
Ok(&self.buffer[self.buf_pos..self.buf_cap])
}
fn consume(&mut self, amt: usize) {
self.buf_pos = self.buf_cap.min(self.buf_pos + amt);
}
}
impl<'a, F: Read + Seek> Read for Stream<'a, F> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let num_bytes = {
let mut buffered_data = self.fill_buf()?;
buffered_data.read(buf)?
};
self.consume(num_bytes);
Ok(num_bytes)
}
}
impl<'a, F: Read + Seek> Seek for Stream<'a, F> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let new_pos: u64 =
match pos {
SeekFrom::Start(delta) => {
if delta > self.total_len {
invalid_input!(
"Cannot seek to {} bytes from start, because stream \
length is only {} bytes",
delta, self.total_len,
);
}
delta
}
SeekFrom::End(delta) => {
if delta > 0 {
invalid_input!(
"Cannot seek to {} bytes past the end of the stream",
delta,
);
} else {
let delta = (-delta) as u64;
if delta > self.total_len {
invalid_input!(
"Cannot seek to {} bytes before end, because \
stream length is only {} bytes",
delta,
self.total_len,
);
}
self.total_len - delta
}
}
SeekFrom::Current(delta) => {
let old_pos = self.current_position();
debug_assert!(old_pos <= self.total_len);
if delta < 0 {
let delta = (-delta) as u64;
if delta > old_pos {
invalid_input!(
"Cannot seek to {} bytes before current position, \
which is only {}",
delta, old_pos,
);
}
old_pos - delta
} else {
let delta = delta as u64;
let remaining = self.total_len - old_pos;
if delta > remaining {
invalid_input!(
"Cannot seek to {} bytes after current position, \
because there are only {} bytes remaining in the \
stream",
delta, remaining,
);
}
old_pos + delta
}
}
};
if new_pos < self.buf_offset_from_start
|| new_pos > self.buf_offset_from_start + self.buf_cap as u64
{
self.flush_changes()?;
self.buf_offset_from_start = new_pos;
self.buf_pos = 0;
self.buf_cap = 0;
} else {
self.buf_pos = (new_pos - self.buf_offset_from_start) as usize;
}
Ok(new_pos)
}
}
impl<'a, F: Read + Write + Seek> Write for Stream<'a, F> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
debug_assert!(self.buf_pos <= self.buffer.len());
if self.buf_pos >= self.buffer.len() {
self.flush_changes()?;
self.buf_offset_from_start += self.buf_pos as u64;
self.buf_pos = 0;
self.buf_cap = 0;
}
let num_bytes_written =
(&mut self.buffer[self.buf_pos..]).write(buf)?;
self.mark_modified();
self.buf_pos += num_bytes_written;
debug_assert!(self.buf_pos <= self.buffer.len());
self.buf_cap = self.buf_cap.max(self.buf_pos);
self.total_len = self
.total_len
.max(self.buf_offset_from_start + self.buf_cap as u64);
Ok(num_bytes_written)
}
fn flush(&mut self) -> io::Result<()> {
self.flush_changes()?;
self.comp.minialloc.flush()
}
}
impl<'a, F> Drop for Stream<'a, F> {
fn drop(&mut self) {
let _ = self.flush_changes();
}
}
trait Flusher<F> {
fn flush_changes(&self, stream: &mut Stream<F>) -> io::Result<()>;
}
struct FlushBuffer;
impl<F: Read + Write + Seek> Flusher<F> for FlushBuffer {
fn flush_changes(&self, stream: &mut Stream<F>) -> io::Result<()> {
stream.comp.write_data_to_stream(
stream.stream_id,
stream.buf_offset_from_start,
&stream.buffer[..stream.buf_cap],
)?;
debug_assert_eq!(
stream.comp.minialloc.dir_entry(stream.stream_id).stream_len,
stream.total_len
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::CompoundFile;
use crate::internal::{consts, DirEntry, Header, Version};
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Cursor};
use std::mem::size_of;
#[test]
fn zero_padded_fat() -> io::Result<()> {
let version = Version::V3;
let mut data = Vec::<u8>::new();
let mut header = Header {
version,
num_dir_sectors: 1,
num_fat_sectors: 1,
first_dir_sector: 1,
first_minifat_sector: consts::END_OF_CHAIN,
num_minifat_sectors: 0,
first_difat_sector: consts::END_OF_CHAIN,
num_difat_sectors: 0,
initial_difat_entries: [consts::FREE_SECTOR;
consts::NUM_DIFAT_ENTRIES_IN_HEADER],
};
header.initial_difat_entries[0] = 0;
header.write_to(&mut data)?;
let fat: Vec<u32> = vec![consts::FAT_SECTOR, consts::END_OF_CHAIN];
for &entry in fat.iter() {
data.write_u32::<LittleEndian>(entry)?;
}
for _ in fat.len()..(version.sector_len() / size_of::<u32>()) {
data.write_u32::<LittleEndian>(0)?;
}
DirEntry::empty_root_entry().write_to(&mut data)?;
for _ in 1..version.dir_entries_per_sector() {
DirEntry::unallocated().write_to(&mut data)?;
}
CompoundFile::open(Cursor::new(data)).expect("open");
Ok(())
}
}