1#![doc = include_str!("../README.md")]
2
3use std::{
4 cell::RefMut,
5 fmt::Debug,
6 fs::{File, OpenOptions},
7 io::{self, BufWriter, Read, Seek, SeekFrom, Write},
8 mem,
9 path::Path,
10 rc::Rc,
11 sync::Mutex,
12};
13use thiserror::Error;
14use tracing::{error, instrument, warn};
15
16pub mod ltp;
17pub mod messaging;
18pub mod ndb;
19
20mod block_sig;
21mod crc;
22mod encode;
23
24use ltp::{heap::*, prop_context::*, table_context::*, tree::*};
25use messaging::{folder::*, message::*, named_prop::*, search::*, store::*};
26use ndb::{
27 block::*, block_id::*, block_ref::*, byte_index::*, header::*, node_id::*, page::*,
28 read_write::*, root::*, *,
29};
30
31#[derive(Error, Debug)]
32pub enum PstError {
33 #[error("Opened read-only")]
34 OpenedReadOnly,
35 #[error("Cannot write to file: {0}")]
36 NoWriteAccess(String),
37 #[error("I/O error: {0:?}")]
38 Io(#[from] io::Error),
39 #[error("I/O error: {0}")]
40 BorrowedIo(String),
41 #[error("Failed to lock file")]
42 LockError,
43 #[error("Integer conversion failed")]
44 IntegerConversion,
45 #[error("Node Database error: {0}")]
46 NodeDatabaseError(#[from] NdbError),
47 #[error("AllocationMapPage not found: {0}")]
48 AllocationMapPageNotFound(usize),
49 #[error("Invalid BTree page: offset: 0x{0:X}")]
50 InvalidBTreePage(u64),
51}
52
53impl From<&PstError> for io::Error {
54 fn from(err: &PstError) -> Self {
55 match err {
56 PstError::NoWriteAccess(path) => {
57 Self::new(io::ErrorKind::PermissionDenied, path.as_str())
58 }
59 err => Self::other(format!("{err:?}")),
60 }
61 }
62}
63
64impl From<PstError> for io::Error {
65 fn from(err: PstError) -> Self {
66 match err {
67 PstError::NoWriteAccess(path) => {
68 Self::new(io::ErrorKind::PermissionDenied, path.as_str())
69 }
70 PstError::Io(err) => err,
71 err => Self::other(err),
72 }
73 }
74}
75
76impl From<&io::Error> for PstError {
77 fn from(err: &io::Error) -> Self {
78 Self::BorrowedIo(format!("{err:?}"))
79 }
80}
81
82type PstResult<T> = std::result::Result<T, PstError>;
83
84trait PstFileLock<Pst>
88where
89 Pst: PstFile,
90{
91 fn start_write(&mut self) -> io::Result<()>;
92 fn finish_write(&mut self) -> io::Result<()>;
93
94 fn block_cache(&self) -> RefMut<'_, RootBTreePageCache<<Pst as PstFile>::BlockBTree>>;
95 fn node_cache(&self) -> RefMut<'_, RootBTreePageCache<<Pst as PstFile>::NodeBTree>>;
96}
97
98pub struct PstFileLockGuard<'a, Pst>
100where
101 Pst: PstFile,
102{
103 pst: &'a mut dyn PstFileLock<Pst>,
104}
105
106impl<'a, Pst> PstFileLockGuard<'a, Pst>
107where
108 Pst: PstFile,
109{
110 fn new(pst: &'a mut dyn PstFileLock<Pst>) -> io::Result<Self> {
111 pst.start_write()?;
112 Ok(Self { pst })
113 }
114
115 #[instrument(skip_all)]
118 pub fn flush(&mut self) -> io::Result<()> {
119 self.pst.finish_write().inspect_err(|err| {
120 error!(
121 name: "PstFinishWriteFailed",
122 ?err,
123 "PstFileLock::finish_write failed"
124 );
125 })?;
126
127 Ok(())
128 }
129}
130
131impl<Pst> Drop for PstFileLockGuard<'_, Pst>
132where
133 Pst: PstFile,
134{
135 #[instrument(skip_all)]
136 fn drop(&mut self) {
137 if let Err(err) = self.flush() {
138 error!(
139 name: "PstFileLockGuardFlushFailed",
140 ?err,
141 "Writing to the PST file failed"
142 );
143 }
144 }
145}
146
147pub trait PstReader: Read + Seek {}
148
149impl<T> PstReader for T where T: Read + Seek {}
150
151pub trait PstFile: Sized {
153 type BlockId: BlockId<Index = Self::BTreeKey> + BlockIdReadWrite;
154 type PageId: BlockId<Index = Self::BTreeKey> + BlockIdReadWrite;
155 type ByteIndex: ByteIndex + ByteIndexReadWrite;
156 type BlockRef: BlockRef<Block = Self::BlockId, Index = Self::ByteIndex> + BlockRefReadWrite;
157 type PageRef: BlockRef<Block = Self::PageId, Index = Self::ByteIndex> + BlockRefReadWrite;
158 type Root: Root<Self>;
159 type Header: Header<Self>;
160 type PageTrailer: PageTrailer<BlockId = Self::PageId> + PageTrailerReadWrite;
161 type BTreeKey: BTreeEntryKey;
162 type NodeBTreeEntry: NodeBTreeEntry<Block = Self::BlockId> + BTreeEntry<Key = Self::BTreeKey>;
163 type NodeBTree: NodeBTree<Self, Self::NodeBTreeEntry>;
164 type BlockBTreeEntry: BlockBTreeEntry<Block = Self::BlockRef> + BTreeEntry<Key = Self::BTreeKey>;
165 type BlockBTree: BlockBTree<Self, Self::BlockBTreeEntry>;
166 type BlockTrailer: BlockTrailer<BlockId = Self::BlockId>;
167 type AllocationMapPage: AllocationMapPage<Self>;
168 type AllocationPageMapPage: AllocationPageMapPage<Self>;
169 type FreeMapPage: FreeMapPage<Self>;
170 type FreePageMapPage: FreePageMapPage<Self>;
171 type DensityListPage: DensityListPage<Self>;
172 type DataTreeEntry: IntermediateTreeEntry + IntermediateDataTreeEntry<Self>;
173 type DataTreeBlock: IntermediateTreeBlock<
174 Header = DataTreeBlockHeader,
175 Entry = Self::DataTreeEntry,
176 Trailer = Self::BlockTrailer,
177 >;
178 type DataBlock: Block<Trailer = Self::BlockTrailer>;
179 type SubNodeTreeBlockHeader: IntermediateTreeHeader;
180 type SubNodeTreeBlock: IntermediateTreeBlock<
181 Header = Self::SubNodeTreeBlockHeader,
182 Entry = IntermediateSubNodeTreeEntry<Self::BlockId>,
183 Trailer = Self::BlockTrailer,
184 >;
185 type SubNodeBlock: IntermediateTreeBlock<
186 Header = Self::SubNodeTreeBlockHeader,
187 Entry = LeafSubNodeTreeEntry<Self::BlockId>,
188 Trailer = Self::BlockTrailer,
189 >;
190 type TableContext: TableContext;
191 type PropertyContext: PropertyContext;
192 type HeapNode: HeapNode;
193 type PropertyTree: HeapTree<Key = PropertyTreeRecordKey, Value = PropertyTreeRecordValue>;
194 type Store: Store;
195 type Folder: Folder;
196 type Message: Message;
197 type NamedPropertyMap: NamedPropertyMap;
198 type SearchUpdateQueue: SearchUpdateQueue;
199
200 fn header(&self) -> &Self::Header;
201 fn density_list(&self) -> Result<&dyn DensityListPage<Self>, &io::Error>;
202 fn reader(&self) -> &Mutex<Box<dyn PstReader>>;
203 fn lock(&mut self) -> io::Result<PstFileLockGuard<'_, Self>>;
204
205 fn read_node(&self, node: NodeId) -> io::Result<Self::NodeBTreeEntry>;
206 fn read_block(&self, block: Self::BlockId) -> io::Result<Vec<u8>>;
207}
208
209struct PstFileInner<Pst>
210where
211 Pst: PstFile,
212{
213 reader: Mutex<Box<dyn PstReader>>,
214 writer: PstResult<Mutex<BufWriter<File>>>,
215 header: Pst::Header,
216 density_list: io::Result<Pst::DensityListPage>,
217 node_cache: NodeBTreePageCache<Pst>,
218 block_cache: BlockBTreePageCache<Pst>,
219}
220
221pub struct UnicodePstFile {
222 inner: PstFileInner<Self>,
223}
224
225impl UnicodePstFile {
226 pub fn read_from(reader: Box<dyn PstReader>) -> io::Result<Self> {
227 let inner = PstFileInner::read_from(reader)?;
228 Ok(Self { inner })
229 }
230
231 pub fn open(path: impl AsRef<Path>) -> io::Result<Self> {
232 let inner = PstFileInner::open(path)?;
233 Ok(Self { inner })
234 }
235}
236
237impl PstFileLock<UnicodePstFile> for UnicodePstFile {
238 fn start_write(&mut self) -> io::Result<()> {
239 self.inner.start_write()
240 }
241
242 fn finish_write(&mut self) -> io::Result<()> {
243 self.inner.finish_write()
244 }
245
246 fn block_cache(&self) -> RefMut<'_, RootBTreePageCache<<Self as PstFile>::BlockBTree>> {
247 self.inner.block_cache.borrow_mut()
248 }
249
250 fn node_cache(&self) -> RefMut<'_, RootBTreePageCache<<Self as PstFile>::NodeBTree>> {
251 self.inner.node_cache.borrow_mut()
252 }
253}
254
255impl PstFile for UnicodePstFile {
256 type BlockId = UnicodeBlockId;
257 type PageId = UnicodePageId;
258 type ByteIndex = UnicodeByteIndex;
259 type BlockRef = UnicodeBlockRef;
260 type PageRef = UnicodePageRef;
261 type Root = UnicodeRoot;
262 type Header = UnicodeHeader;
263 type PageTrailer = UnicodePageTrailer;
264 type BTreeKey = u64;
265 type NodeBTreeEntry = UnicodeNodeBTreeEntry;
266 type NodeBTree = UnicodeNodeBTree;
267 type BlockBTreeEntry = UnicodeBlockBTreeEntry;
268 type BlockBTree = UnicodeBlockBTree;
269 type BlockTrailer = UnicodeBlockTrailer;
270 type AllocationMapPage = UnicodeMapPage<{ PageType::AllocationMap as u8 }>;
271 type AllocationPageMapPage = UnicodeMapPage<{ PageType::AllocationPageMap as u8 }>;
272 type FreeMapPage = UnicodeMapPage<{ PageType::FreeMap as u8 }>;
273 type FreePageMapPage = UnicodeMapPage<{ PageType::FreePageMap as u8 }>;
274 type DensityListPage = UnicodeDensityListPage;
275 type DataTreeEntry = UnicodeDataTreeEntry;
276 type DataTreeBlock = UnicodeDataTreeBlock;
277 type DataBlock = UnicodeDataBlock;
278 type SubNodeTreeBlockHeader = UnicodeSubNodeTreeBlockHeader;
279 type SubNodeTreeBlock = UnicodeIntermediateSubNodeTreeBlock;
280 type SubNodeBlock = UnicodeLeafSubNodeTreeBlock;
281 type HeapNode = UnicodeHeapNode;
282 type PropertyTree = UnicodeHeapTree<PropertyTreeRecordKey, PropertyTreeRecordValue>;
283 type TableContext = UnicodeTableContext;
284 type PropertyContext = UnicodePropertyContext;
285 type Store = UnicodeStore;
286 type Folder = UnicodeFolder;
287 type Message = UnicodeMessage;
288 type NamedPropertyMap = UnicodeNamedPropertyMap;
289 type SearchUpdateQueue = UnicodeSearchUpdateQueue;
290
291 fn header(&self) -> &Self::Header {
292 &self.inner.header
293 }
294
295 fn density_list(&self) -> Result<&dyn DensityListPage<Self>, &io::Error> {
296 self.inner.density_list.as_ref().map(|dl| dl as _)
297 }
298
299 fn reader(&self) -> &Mutex<Box<dyn PstReader>> {
300 &self.inner.reader
301 }
302
303 fn lock(&mut self) -> io::Result<PstFileLockGuard<'_, Self>> {
304 PstFileLockGuard::new(self)
305 }
306
307 fn read_node(&self, node: NodeId) -> io::Result<UnicodeNodeBTreeEntry> {
308 self.inner.read_node(node)
309 }
310
311 fn read_block(&self, block: UnicodeBlockId) -> io::Result<Vec<u8>> {
312 self.inner.read_block(block)
313 }
314}
315
316pub struct AnsiPstFile {
317 inner: PstFileInner<Self>,
318}
319
320impl AnsiPstFile {
321 pub fn read_from(reader: Box<dyn PstReader>) -> io::Result<Self> {
322 let inner = PstFileInner::read_from(reader)?;
323 Ok(Self { inner })
324 }
325
326 pub fn open(path: impl AsRef<Path>) -> io::Result<Self> {
327 let inner = PstFileInner::open(path)?;
328 Ok(Self { inner })
329 }
330}
331
332impl PstFileLock<AnsiPstFile> for AnsiPstFile {
333 fn start_write(&mut self) -> io::Result<()> {
334 self.inner.start_write()
335 }
336
337 fn finish_write(&mut self) -> io::Result<()> {
338 self.inner.finish_write()
339 }
340
341 fn block_cache(&self) -> RefMut<'_, RootBTreePageCache<<Self as PstFile>::BlockBTree>> {
342 self.inner.block_cache.borrow_mut()
343 }
344
345 fn node_cache(&self) -> RefMut<'_, RootBTreePageCache<<Self as PstFile>::NodeBTree>> {
346 self.inner.node_cache.borrow_mut()
347 }
348}
349
350impl PstFile for AnsiPstFile {
351 type BlockId = AnsiBlockId;
352 type PageId = AnsiPageId;
353 type ByteIndex = AnsiByteIndex;
354 type BlockRef = AnsiBlockRef;
355 type PageRef = AnsiPageRef;
356 type Root = AnsiRoot;
357 type Header = AnsiHeader;
358 type PageTrailer = AnsiPageTrailer;
359 type BTreeKey = u32;
360 type NodeBTreeEntry = AnsiNodeBTreeEntry;
361 type NodeBTree = AnsiNodeBTree;
362 type BlockBTreeEntry = AnsiBlockBTreeEntry;
363 type BlockBTree = AnsiBlockBTree;
364 type BlockTrailer = AnsiBlockTrailer;
365 type AllocationMapPage = AnsiMapPage<{ PageType::AllocationMap as u8 }>;
366 type AllocationPageMapPage = AnsiMapPage<{ PageType::AllocationPageMap as u8 }>;
367 type FreeMapPage = AnsiMapPage<{ PageType::FreeMap as u8 }>;
368 type FreePageMapPage = AnsiMapPage<{ PageType::FreePageMap as u8 }>;
369 type DensityListPage = AnsiDensityListPage;
370 type DataTreeEntry = AnsiDataTreeEntry;
371 type DataTreeBlock = AnsiDataTreeBlock;
372 type DataBlock = AnsiDataBlock;
373 type SubNodeTreeBlockHeader = AnsiSubNodeTreeBlockHeader;
374 type SubNodeTreeBlock = AnsiIntermediateSubNodeTreeBlock;
375 type SubNodeBlock = AnsiLeafSubNodeTreeBlock;
376 type HeapNode = AnsiHeapNode;
377 type PropertyTree = AnsiHeapTree<PropertyTreeRecordKey, PropertyTreeRecordValue>;
378 type TableContext = AnsiTableContext;
379 type PropertyContext = AnsiPropertyContext;
380 type Store = AnsiStore;
381 type Folder = AnsiFolder;
382 type Message = AnsiMessage;
383 type NamedPropertyMap = AnsiNamedPropertyMap;
384 type SearchUpdateQueue = AnsiSearchUpdateQueue;
385
386 fn header(&self) -> &Self::Header {
387 &self.inner.header
388 }
389
390 fn density_list(&self) -> Result<&dyn DensityListPage<Self>, &io::Error> {
391 self.inner.density_list.as_ref().map(|dl| dl as _)
392 }
393
394 fn reader(&self) -> &Mutex<Box<dyn PstReader>> {
395 &self.inner.reader
396 }
397
398 fn lock(&mut self) -> io::Result<PstFileLockGuard<'_, Self>> {
399 PstFileLockGuard::new(self)
400 }
401
402 fn read_node(&self, node: NodeId) -> io::Result<AnsiNodeBTreeEntry> {
403 self.inner.read_node(node)
404 }
405
406 fn read_block(&self, block: AnsiBlockId) -> io::Result<Vec<u8>> {
407 self.inner.read_block(block)
408 }
409}
410
411const AMAP_FIRST_OFFSET: u64 = 0x4400;
412const AMAP_DATA_SIZE: u64 = size_of::<MapBits>() as u64 * 8 * 64;
413
414const PMAP_FIRST_OFFSET: u64 = AMAP_FIRST_OFFSET + PAGE_SIZE as u64;
415const PMAP_PAGE_COUNT: u64 = 8;
416const PMAP_DATA_SIZE: u64 = AMAP_DATA_SIZE * PMAP_PAGE_COUNT;
417
418const FMAP_FIRST_SIZE: u64 = 128;
419const FMAP_FIRST_DATA_SIZE: u64 = AMAP_DATA_SIZE * FMAP_FIRST_SIZE;
420const FMAP_FIRST_OFFSET: u64 = AMAP_FIRST_OFFSET + FMAP_FIRST_DATA_SIZE + (2 * PAGE_SIZE) as u64;
421const FMAP_PAGE_COUNT: u64 = size_of::<MapBits>() as u64;
422const FMAP_DATA_SIZE: u64 = AMAP_DATA_SIZE * FMAP_PAGE_COUNT;
423
424const FPMAP_FIRST_SIZE: u64 = 128 * 64;
425const FPMAP_FIRST_DATA_SIZE: u64 = AMAP_DATA_SIZE * FPMAP_FIRST_SIZE;
426const FPMAP_FIRST_OFFSET: u64 = AMAP_FIRST_OFFSET + FPMAP_FIRST_DATA_SIZE + (3 * PAGE_SIZE) as u64;
427const FPMAP_PAGE_COUNT: u64 = size_of::<MapBits>() as u64 * 64;
428const FPMAP_DATA_SIZE: u64 = AMAP_DATA_SIZE * FPMAP_PAGE_COUNT;
429
430struct AllocationMapPageInfo<Pst>
431where
432 Pst: PstFile,
433 <Pst as PstFile>::AllocationMapPage: AllocationMapPageReadWrite<Pst>,
434{
435 amap_page: <Pst as PstFile>::AllocationMapPage,
436 free_space: u64,
437}
438
439impl<Pst> AllocationMapPageInfo<Pst>
440where
441 Pst: PstFile,
442 <Pst as PstFile>::AllocationMapPage: AllocationMapPageReadWrite<Pst>,
443{
444 fn max_free_slots(&self) -> u8 {
445 u8::try_from(self.amap_page.find_free_bits(0xFF).len()).unwrap_or(0xFF)
446 }
447}
448
449type PstFileReadWriteBTree<Pst, BTree> = RootBTreePage<
450 Pst,
451 <BTree as RootBTree>::Entry,
452 <BTree as RootBTree>::IntermediatePage,
453 <BTree as RootBTree>::LeafPage,
454>;
455
456type PstFileReadWriteNodeBTree<Pst> = PstFileReadWriteBTree<Pst, <Pst as PstFile>::NodeBTree>;
457
458type PstFileReadWriteBlockBTree<Pst> = PstFileReadWriteBTree<Pst, <Pst as PstFile>::BlockBTree>;
459
460impl<Pst> PstFileInner<Pst>
461where
462 Pst: PstFile + PstFileLock<Pst>,
463 <Pst as PstFile>::BlockId: BlockId<Index = <Pst as PstFile>::BTreeKey>
464 + From<<<Pst as PstFile>::ByteIndex as ByteIndex>::Index>
465 + Debug,
466 <Pst as PstFile>::PageId: From<<<Pst as PstFile>::ByteIndex as ByteIndex>::Index> + Debug,
467 <Pst as PstFile>::ByteIndex: ByteIndex<Index: TryFrom<u64>> + Debug,
468 <Pst as PstFile>::BlockRef: Debug,
469 <Pst as PstFile>::PageRef: Debug,
470 <Pst as PstFile>::Root: RootReadWrite<Pst>,
471 <Pst as PstFile>::Header: HeaderReadWrite<Pst>,
472 <Pst as PstFile>::DensityListPage: DensityListPageReadWrite<Pst>,
473 <Pst as PstFile>::PageTrailer: PageTrailerReadWrite,
474 <Pst as PstFile>::BTreeKey: BTreePageKeyReadWrite,
475 <Pst as PstFile>::NodeBTreeEntry: NodeBTreeEntryReadWrite,
476 <Pst as PstFile>::NodeBTree: NodeBTreeReadWrite<Pst, <Pst as PstFile>::NodeBTreeEntry>,
477 <<Pst as PstFile>::NodeBTree as RootBTree>::IntermediatePage:
478 RootBTreeIntermediatePageReadWrite<
479 Pst,
480 <Pst as PstFile>::NodeBTreeEntry,
481 <<Pst as PstFile>::NodeBTree as RootBTree>::LeafPage,
482 >,
483 <<<Pst as PstFile>::NodeBTree as RootBTree>::IntermediatePage as BTreePage>::Entry:
484 BTreePageEntryReadWrite,
485 <<Pst as PstFile>::NodeBTree as RootBTree>::LeafPage: RootBTreeLeafPageReadWrite<Pst>,
486 <Pst as PstFile>::BlockBTreeEntry: BlockBTreeEntryReadWrite,
487 <Pst as PstFile>::BlockBTree: BlockBTreeReadWrite<Pst, <Pst as PstFile>::BlockBTreeEntry>,
488 <<Pst as PstFile>::BlockBTree as RootBTree>::IntermediatePage:
489 RootBTreeIntermediatePageReadWrite<
490 Pst,
491 <Pst as PstFile>::BlockBTreeEntry,
492 <<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage,
493 >,
494 <<<Pst as PstFile>::BlockBTree as RootBTree>::IntermediatePage as BTreePage>::Entry:
495 BTreePageEntryReadWrite,
496 <<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage: RootBTreeLeafPageReadWrite<Pst>,
497 <Pst as PstFile>::BlockTrailer: BlockTrailerReadWrite,
498 <Pst as PstFile>::AllocationMapPage: AllocationMapPageReadWrite<Pst>,
499 <Pst as PstFile>::AllocationPageMapPage: AllocationPageMapPageReadWrite<Pst>,
500 <Pst as PstFile>::FreeMapPage: FreeMapPageReadWrite<Pst>,
501 <Pst as PstFile>::FreePageMapPage: FreePageMapPageReadWrite<Pst>,
502 <Pst as PstFile>::DensityListPage: DensityListPageReadWrite<Pst>,
503 <Pst as PstFile>::DataTreeBlock: IntermediateTreeBlockReadWrite,
504 <Pst as PstFile>::DataTreeEntry:
505 IntermediateTreeEntryReadWrite + From<<Pst as PstFile>::BlockId>,
506 <Pst as PstFile>::DataBlock: BlockReadWrite + Clone,
507 <Pst as PstFile>::SubNodeTreeBlockHeader: SubNodeTreeBlockHeaderReadWrite,
508 <Pst as PstFile>::SubNodeTreeBlock: IntermediateTreeBlockReadWrite,
509 <<Pst as PstFile>::SubNodeTreeBlock as IntermediateTreeBlock>::Entry:
510 IntermediateTreeEntryReadWrite,
511 <Pst as PstFile>::SubNodeBlock: IntermediateTreeBlockReadWrite,
512 <<Pst as PstFile>::SubNodeBlock as IntermediateTreeBlock>::Entry:
513 IntermediateTreeEntryReadWrite,
514{
515 fn read_from(mut reader: Box<dyn PstReader>) -> io::Result<Self> {
516 let header = <<Pst as PstFile>::Header as HeaderReadWrite<Pst>>::read(&mut reader)?;
517 let density_list =
518 <<Pst as PstFile>::DensityListPage as DensityListPageReadWrite<Pst>>::read(&mut reader);
519 Ok(Self {
520 reader: Mutex::new(Box::new(reader)),
521 writer: Err(PstError::OpenedReadOnly),
522 header,
523 density_list,
524 node_cache: Default::default(),
525 block_cache: Default::default(),
526 })
527 }
528
529 fn open(path: impl AsRef<Path>) -> io::Result<Self> {
530 let reader = Box::new(File::open(&path)?);
531 let writer = OpenOptions::new()
532 .write(true)
533 .open(&path)
534 .map(BufWriter::new)
535 .map(Mutex::new)
536 .map_err(|_| PstError::NoWriteAccess(path.as_ref().display().to_string()));
537 Ok(Self {
538 writer,
539 ..Self::read_from(reader)?
540 })
541 }
542
543 fn start_write(&mut self) -> io::Result<()> {
548 self.rebuild_allocation_map()?;
549 self.ensure_density_list()?;
550
551 let header = {
552 self.header.update_unique();
553
554 let root = self.header.root_mut();
555 root.set_amap_status(AmapStatus::Invalid);
556 self.header.clone()
557 };
558
559 let mut writer = self
560 .writer
561 .as_ref()?
562 .lock()
563 .map_err(|_| PstError::LockError)?;
564 let writer = &mut *writer;
565 writer.seek(SeekFrom::Start(0))?;
566 header.write(writer)?;
567 writer.flush()
568 }
569
570 #[instrument(skip_all)]
575 fn finish_write(&mut self) -> io::Result<()> {
576 let header = {
579 self.header.update_unique();
580 let root = self.header.root_mut();
581 root.set_amap_status(AmapStatus::Valid2);
582 self.header.clone()
583 };
584
585 self.update_density_list_page_id()?;
586 let density_list = {
587 self.density_list.as_ref().ok().and_then(|dl| {
588 <<Pst as PstFile>::DensityListPage as DensityListPageReadWrite<Pst>>::new(
589 dl.backfill_complete(),
590 dl.current_page(),
591 dl.entries(),
592 *dl.trailer(),
593 )
594 .ok()
595 })
596 };
597
598 let mut writer = self
599 .writer
600 .as_ref()?
601 .lock()
602 .map_err(|_| PstError::LockError)?;
603 let writer = &mut *writer;
604 writer.seek(SeekFrom::Start(0))?;
605 header.write(writer)?;
606 writer.flush()?;
607
608 if let Some(density_list) = density_list {
609 density_list.write(writer)?;
610 writer.flush()?;
611 }
612
613 Ok(())
614 }
615
616 fn rebuild_allocation_map(&mut self) -> io::Result<()> {
618 let root = self.header.root();
619 if AmapStatus::Invalid != root.amap_is_valid() {
620 return Ok(());
621 }
622
623 let num_amap_pages = root.file_eof_index().index().into() - AMAP_FIRST_OFFSET;
624 let num_amap_pages = num_amap_pages.div_ceil(AMAP_DATA_SIZE);
625
626 let mut amap_pages: Vec<_> = (0..num_amap_pages)
627 .map(|index| {
628 let has_pmap_page = index % 8 == 0;
629 let has_fmap_page = has_pmap_page
630 && index >= FMAP_FIRST_SIZE
631 && (index - FMAP_FIRST_SIZE) % FMAP_PAGE_COUNT == 0;
632 let has_fpmap_page = has_pmap_page
633 && index >= FPMAP_FIRST_SIZE
634 && (index - FPMAP_FIRST_SIZE) % FPMAP_PAGE_COUNT == 0;
635
636 let index =
637 <<<Pst as PstFile>::ByteIndex as ByteIndex>::Index as TryFrom<u64>>::try_from(
638 index * AMAP_DATA_SIZE + AMAP_FIRST_OFFSET,
639 )
640 .map_err(|_| PstError::IntegerConversion)?;
641 let block_id = <Pst as PstFile>::PageId::from(index);
642
643 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
644 PageType::AllocationMap,
645 0,
646 block_id,
647 0,
648 );
649
650 let mut map_bits = [0; mem::size_of::<MapBits>()];
651 let mut reserved = 1;
652 if has_pmap_page {
653 reserved += 1;
654 }
655 if has_fmap_page {
656 reserved += 1;
657 }
658 if has_fpmap_page {
659 reserved += 1;
660 }
661
662 let free_space = AMAP_DATA_SIZE - (reserved * PAGE_SIZE) as u64;
663
664 let reserved = &[0xFF; 4][..reserved];
665 map_bits[..reserved.len()].copy_from_slice(reserved);
666
667 let amap_page =
668 <<Pst as PstFile>::AllocationMapPage as AllocationMapPageReadWrite<Pst>>::new(
669 map_bits, trailer,
670 )?;
671 Ok(AllocationMapPageInfo::<Pst> {
672 amap_page,
673 free_space,
674 })
675 })
676 .collect::<PstResult<Vec<_>>>()?;
677
678 {
679 let mut reader = self.reader.lock().map_err(|_| PstError::LockError)?;
680 let reader = &mut *reader;
681
682 let node_btree =
683 <Pst::NodeBTree as RootBTreeReadWrite>::read(reader, *root.node_btree())?;
684
685 Self::mark_node_btree_allocations(
686 reader,
687 root.node_btree().index(),
688 &node_btree,
689 &mut amap_pages,
690 )?;
691
692 let block_btree =
693 <Pst::BlockBTree as RootBTreeReadWrite>::read(reader, *root.block_btree())?;
694
695 Self::mark_block_btree_allocations(
696 reader,
697 root.block_btree().index(),
698 &block_btree,
699 &mut amap_pages,
700 )?;
701 }
702
703 let free_bytes =
704 <<<Pst as PstFile>::ByteIndex as ByteIndex>::Index as TryFrom<u64>>::try_from(
705 amap_pages.iter().map(|page| page.free_space).sum(),
706 )
707 .map_err(|_| PstError::IntegerConversion)?;
708 let free_bytes = <<Pst as PstFile>::ByteIndex as ByteIndexReadWrite>::new(free_bytes);
709
710 let mut first_fmap = [0; FMAP_FIRST_SIZE as usize];
711 for (entry, free_space) in first_fmap
712 .iter_mut()
713 .zip(amap_pages.iter().map(|page| page.max_free_slots()))
714 {
715 *entry = free_space;
716 }
717
718 let pmap_pages: Vec<_> = (0..=(num_amap_pages / 8))
719 .map(|index| {
720 let index =
721 <<<Pst as PstFile>::ByteIndex as ByteIndex>::Index as TryFrom<u64>>::try_from(
722 index * PMAP_DATA_SIZE + PMAP_FIRST_OFFSET,
723 )
724 .map_err(|_| PstError::IntegerConversion)?;
725 let block_id = <Pst as PstFile>::PageId::from(index);
726
727 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
728 PageType::AllocationPageMap,
729 0,
730 block_id,
731 0,
732 );
733
734 let map_bits = [0xFF; mem::size_of::<MapBits>()];
735
736 let pmap_page =
737 <<Pst as PstFile>::AllocationPageMapPage as AllocationPageMapPageReadWrite<
738 Pst,
739 >>::new(map_bits, trailer)?;
740 Ok(pmap_page)
741 })
742 .collect::<PstResult<Vec<_>>>()?;
743
744 let fmap_pages: Vec<_> = (0..(num_amap_pages.max(FMAP_FIRST_SIZE) - FMAP_FIRST_SIZE)
745 .div_ceil(FMAP_PAGE_COUNT))
746 .map(|index| {
747 let amap_index =
748 FMAP_FIRST_SIZE as usize + (index as usize * mem::size_of::<MapBits>());
749 let index =
750 <<<Pst as PstFile>::ByteIndex as ByteIndex>::Index as TryFrom<u64>>::try_from(
751 index * FMAP_DATA_SIZE + FMAP_FIRST_OFFSET,
752 )
753 .map_err(|_| PstError::IntegerConversion)?;
754 let block_id = <Pst as PstFile>::PageId::from(index);
755
756 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
757 PageType::FreeMap,
758 0,
759 block_id,
760 0,
761 );
762
763 let mut map_bits = [0; mem::size_of::<MapBits>()];
764 for (entry, free_space) in map_bits.iter_mut().zip(
765 amap_pages
766 .iter()
767 .skip(amap_index)
768 .map(|page| page.max_free_slots()),
769 ) {
770 *entry = free_space;
771 }
772
773 let fmap_page = <<Pst as PstFile>::FreeMapPage as FreeMapPageReadWrite<Pst>>::new(
774 map_bits, trailer,
775 )?;
776 Ok(fmap_page)
777 })
778 .collect::<PstResult<Vec<_>>>()?;
779
780 let fpmap_pages: Vec<_> = (0..(num_amap_pages.max(FPMAP_FIRST_SIZE) - FPMAP_FIRST_SIZE)
781 .div_ceil(FPMAP_PAGE_COUNT))
782 .map(|index| {
783 let index =
784 <<<Pst as PstFile>::ByteIndex as ByteIndex>::Index as TryFrom<u64>>::try_from(
785 index * FPMAP_DATA_SIZE + FPMAP_FIRST_OFFSET,
786 )
787 .map_err(|_| PstError::IntegerConversion)?;
788 let block_id = <Pst as PstFile>::PageId::from(index);
789
790 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
791 PageType::FreePageMap,
792 0,
793 block_id,
794 0,
795 );
796
797 let map_bits = [0xFF; mem::size_of::<MapBits>()];
798
799 let fpmap_page = <<Pst as PstFile>::FreePageMapPage as FreePageMapPageReadWrite<
800 Pst,
801 >>::new(map_bits, trailer)?;
802 Ok(fpmap_page)
803 })
804 .collect::<PstResult<Vec<_>>>()?;
805
806 {
807 let mut writer = self
808 .writer
809 .as_ref()?
810 .lock()
811 .map_err(|_| PstError::LockError)?;
812 let writer = &mut *writer;
813
814 for page in amap_pages.into_iter().map(|info| info.amap_page) {
815 writer.seek(SeekFrom::Start(page.trailer().block_id().into_u64()))?;
816 <Pst::AllocationMapPage as AllocationMapPageReadWrite<Pst>>::write(&page, writer)?;
817 }
818
819 for page in pmap_pages.into_iter() {
820 writer.seek(SeekFrom::Start(page.trailer().block_id().into_u64()))?;
821 <Pst::AllocationPageMapPage as AllocationPageMapPageReadWrite<Pst>>::write(
822 &page, writer,
823 )?;
824 }
825
826 for page in fmap_pages.into_iter() {
827 writer.seek(SeekFrom::Start(page.trailer().block_id().into_u64()))?;
828 <Pst::FreeMapPage as FreeMapPageReadWrite<Pst>>::write(&page, writer)?;
829 }
830
831 for page in fpmap_pages.into_iter() {
832 writer.seek(SeekFrom::Start(page.trailer().block_id().into_u64()))?;
833 <Pst::FreePageMapPage as FreePageMapPageReadWrite<Pst>>::write(&page, writer)?;
834 }
835
836 writer.flush()?;
837 }
838
839 let header = {
840 <<Pst as PstFile>::Header as HeaderReadWrite<Pst>>::first_free_map(&mut self.header)
841 .copy_from_slice(&first_fmap);
842 self.header.update_unique();
843
844 let root = self.header.root_mut();
845 root.reset_free_size(free_bytes)?;
846 root.set_amap_status(AmapStatus::Valid2);
847
848 self.header.clone()
849 };
850
851 let mut writer = self
852 .writer
853 .as_ref()?
854 .lock()
855 .map_err(|_| PstError::LockError)?;
856 let writer = &mut *writer;
857 writer.seek(SeekFrom::Start(0))?;
858 header.write(writer)?;
859 writer.flush()
860 }
861
862 #[instrument(skip_all)]
868 fn mark_node_btree_allocations<R: PstReader>(
869 reader: &mut R,
870 page_index: Pst::ByteIndex,
871 node_btree: &PstFileReadWriteNodeBTree<Pst>,
872 amap_pages: &mut Vec<AllocationMapPageInfo<Pst>>,
873 ) -> io::Result<()> {
874 Self::mark_page_allocation(page_index.index().into(), amap_pages)?;
875
876 if let RootBTreePage::Intermediate(page, ..) = node_btree {
877 let level = page.level();
878 for entry in page.entries() {
879 let block = entry.block();
880 let node_btree = <Pst::NodeBTree as RootBTreeReadWrite>::read(reader, block)?;
881 match &node_btree {
882 RootBTreePage::Intermediate(page, ..) if page.level() + 1 != level => {
883 error!(
884 name: "PstUnexpectedBTreeIntermediatePage",
885 block = ?block.block(),
886 index = ?block.index(),
887 parent = level,
888 child = page.level(),
889 "Possible NBT page cycle detected, expected child == parent - 1"
890 );
891 return Err(PstError::InvalidBTreePage(block.index().index().into()).into());
892 }
893 RootBTreePage::Leaf(_) if level != 1 => {
894 error!(
895 name: "PstUnexpectedBTreeLeafPage",
896 block = ?block.block(),
897 index = ?block.index(),
898 parent = level,
899 child = page.level(),
900 "Corrupted NBT intermediate page detected, unexpected child leaf page"
901 );
902 return Err(PstError::InvalidBTreePage(block.index().index().into()).into());
903 }
904 _ => (),
905 }
906 Self::mark_node_btree_allocations(reader, block.index(), &node_btree, amap_pages)?;
907 }
908 }
909
910 Ok(())
911 }
912
913 #[instrument(skip_all)]
917 fn mark_block_btree_allocations<R: PstReader>(
918 reader: &mut R,
919 page_index: Pst::ByteIndex,
920 block_btree: &PstFileReadWriteBlockBTree<Pst>,
921 amap_pages: &mut Vec<AllocationMapPageInfo<Pst>>,
922 ) -> io::Result<()> {
923 Self::mark_page_allocation(page_index.index().into(), amap_pages)?;
924
925 match block_btree {
926 RootBTreePage::Intermediate(page, ..) => {
927 let level = page.level();
928 for entry in page.entries() {
929 let block = entry.block();
930 let block_btree = <Pst::BlockBTree as RootBTreeReadWrite>::read(reader, block)?;
931 match &block_btree {
932 RootBTreePage::Intermediate(page, ..) if page.level() + 1 != level => {
933 error!(
934 name: "PstUnexpectedBTreeIntermediatePage",
935 block = ?block.block(),
936 index = ?block.index(),
937 parent = level,
938 child = page.level(),
939 "Possible BBT page cycle detected, expected child == parent - 1"
940 );
941 return Err(
942 PstError::InvalidBTreePage(block.index().index().into()).into()
943 );
944 }
945 RootBTreePage::Leaf(_) if level != 1 => {
946 error!(
947 name: "PstUnexpectedBTreeLeafPage",
948 block = ?block.block(),
949 index = ?block.index(),
950 parent = level,
951 child = page.level(),
952 "Corrupted BBT intermediate page detected, unexpected child leaf page"
953 );
954 return Err(
955 PstError::InvalidBTreePage(block.index().index().into()).into()
956 );
957 }
958 _ => (),
959 }
960 Self::mark_block_btree_allocations(
961 reader,
962 block.index(),
963 &block_btree,
964 amap_pages,
965 )?;
966 }
967 }
968 RootBTreePage::Leaf(page) => {
969 for entry in page.entries() {
970 Self::mark_block_allocation(
971 entry.block().index().index().into(),
972 entry.size(),
973 amap_pages,
974 )?;
975 }
976 }
977 }
978 Ok(())
979 }
980
981 fn mark_page_allocation(
983 index: u64,
984 amap_pages: &mut [AllocationMapPageInfo<Pst>],
985 ) -> io::Result<()> {
986 let index = index - AMAP_FIRST_OFFSET;
987 let amap_index =
988 usize::try_from(index / AMAP_DATA_SIZE).map_err(|_| PstError::IntegerConversion)?;
989 let entry = amap_pages
990 .get_mut(amap_index)
991 .ok_or(PstError::AllocationMapPageNotFound(amap_index))?;
992 entry.free_space -= PAGE_SIZE as u64;
993
994 let bytes = entry.amap_page.map_bits_mut();
995
996 let bit_index = usize::try_from((index % AMAP_DATA_SIZE) / 64)
997 .map_err(|_| PstError::IntegerConversion)?;
998 let byte_index = bit_index / 8;
999 let bit_index = bit_index % 8;
1000
1001 if bit_index == 0 {
1002 bytes[byte_index] = 0xFF;
1003 } else {
1004 let mask = 0x80_u8 >> bit_index;
1005 let mask = mask | (mask - 1);
1006 bytes[byte_index] |= mask;
1007 bytes[byte_index + 1] |= !mask;
1008 }
1009
1010 Ok(())
1011 }
1012
1013 fn mark_block_allocation(
1015 index: u64,
1016 size: u16,
1017 amap_pages: &mut [AllocationMapPageInfo<Pst>],
1018 ) -> io::Result<()> {
1019 let index = index - AMAP_FIRST_OFFSET;
1020 let amap_index =
1021 usize::try_from(index / AMAP_DATA_SIZE).map_err(|_| PstError::IntegerConversion)?;
1022 let entry = amap_pages
1023 .get_mut(amap_index)
1024 .ok_or(PstError::AllocationMapPageNotFound(amap_index))?;
1025 let size = u64::from(block_size(
1026 size + <<Pst as PstFile>::BlockTrailer as BlockTrailerReadWrite>::SIZE,
1027 ));
1028 entry.free_space -= size;
1029
1030 let bytes = entry.amap_page.map_bits_mut();
1031
1032 let bit_start = usize::try_from((index % AMAP_DATA_SIZE) / 64)
1033 .map_err(|_| PstError::IntegerConversion)?;
1034 let bit_end =
1035 bit_start + usize::try_from(size / 64).map_err(|_| PstError::IntegerConversion)?;
1036 let byte_start = bit_start / 8;
1037 let bit_start = bit_start % 8;
1038 let byte_end = bit_end / 8;
1039 let bit_end = bit_end % 8;
1040
1041 if byte_start == byte_end {
1042 if bit_end > bit_start {
1044 let mask_start = 0x80_u8 >> bit_start;
1045 let mask_start = mask_start | (mask_start - 1);
1046 let mask_end = 0x80_u8 >> bit_end;
1047 let mask_end = !(mask_end | (mask_end - 1));
1048 let mask = mask_start & mask_end;
1049 bytes[byte_start] |= mask;
1050 }
1051 return Ok(());
1052 }
1053
1054 let byte_start = if bit_start == 0 {
1055 byte_start
1056 } else {
1057 let mask_start = 0x80_u8 >> bit_start;
1058 let mask_start = mask_start | (mask_start - 1);
1059 bytes[byte_start] |= mask_start;
1060 byte_start + 1
1061 };
1062
1063 if bit_end != 0 {
1064 let mask_end = 0x80_u8 >> bit_end;
1065 let mask_end = !(mask_end | (mask_end - 1));
1066 bytes[byte_end] |= mask_end;
1067 };
1068
1069 if byte_end > byte_start {
1070 for byte in bytes[byte_start..byte_end].iter_mut() {
1071 *byte = 0xFF;
1072 }
1073 }
1074
1075 Ok(())
1076 }
1077
1078 fn ensure_density_list(&mut self) -> PstResult<()> {
1081 if let Ok(density_list) = self.density_list.as_ref() {
1082 if density_list.trailer().block_id() == self.header.next_page() {
1083 return Ok(());
1084 }
1085 }
1086
1087 let current_page = u32::try_from(
1088 (self.header.root().amap_last_index().index().into() - AMAP_FIRST_OFFSET)
1089 / AMAP_DATA_SIZE,
1090 )
1091 .map_err(|_| PstError::IntegerConversion)?;
1092 let block_id = self.header.next_page();
1093 let signature = PageType::DensityList
1094 .signature(ndb::page::DENSITY_LIST_FILE_OFFSET, block_id.into_u64());
1095 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
1096 PageType::DensityList,
1097 signature,
1098 block_id,
1099 0,
1100 );
1101 let density_list =
1102 <<Pst as PstFile>::DensityListPage as DensityListPageReadWrite<Pst>>::new(
1103 false,
1104 current_page,
1105 &[],
1106 trailer,
1107 )?;
1108
1109 self.density_list = Ok(density_list);
1110 Ok(())
1111 }
1112
1113 fn update_density_list_page_id(&mut self) -> PstResult<()> {
1116 let Ok(density_list) = self.density_list.as_ref() else {
1117 return Ok(());
1118 };
1119
1120 let next_page = self.header.next_page();
1121 if density_list.trailer().block_id() == next_page {
1122 return Ok(());
1123 }
1124
1125 let signature = PageType::DensityList
1126 .signature(ndb::page::DENSITY_LIST_FILE_OFFSET, next_page.into_u64());
1127 let trailer = <<Pst as PstFile>::PageTrailer as PageTrailerReadWrite>::new(
1128 PageType::DensityList,
1129 signature,
1130 next_page,
1131 0,
1132 );
1133
1134 let density_list =
1135 <<Pst as PstFile>::DensityListPage as DensityListPageReadWrite<Pst>>::new(
1136 density_list.backfill_complete(),
1137 density_list.current_page(),
1138 density_list.entries(),
1139 trailer,
1140 )?;
1141
1142 self.density_list = Ok(density_list);
1143 Ok(())
1144 }
1145
1146 fn read_node(&self, node: NodeId) -> io::Result<<Pst as PstFile>::NodeBTreeEntry> {
1147 let node_btree = *self.header.root().node_btree();
1148 let mut reader = self.reader.lock().map_err(|_| PstError::LockError)?;
1149 let reader = &mut *reader;
1150 let node_btree =
1151 <<Pst as PstFile>::NodeBTree as RootBTreeReadWrite>::read(reader, node_btree)?;
1152 let mut page_cache = self.node_cache.borrow_mut();
1153 let node_id: <Pst as PstFile>::BTreeKey = u32::from(node).into();
1154 let node = node_btree.find_entry(reader, node_id, &mut page_cache)?;
1155 Ok(node)
1156 }
1157
1158 fn read_block(&self, block: <Pst as PstFile>::BlockId) -> io::Result<Vec<u8>> {
1159 let encoding = self.header.crypt_method();
1160 let block_btree = *self.header.root().block_btree();
1161 let mut reader = self.reader.lock().map_err(|_| PstError::LockError)?;
1162 let reader = &mut *reader;
1163 let block_btree =
1164 <<Pst as PstFile>::BlockBTree as RootBTreeReadWrite>::read(reader, block_btree)?;
1165 let mut page_cache = self.block_cache.borrow_mut();
1166 let block = block_btree.find_entry(reader, block.search_key(), &mut page_cache)?;
1167 let block = DataTree::<Pst>::read(reader, encoding, &block)?;
1168 let mut block_cache = Default::default();
1169 let mut data = vec![];
1170 let _ = block
1171 .reader(
1172 reader,
1173 encoding,
1174 &block_btree,
1175 &mut page_cache,
1176 &mut block_cache,
1177 )?
1178 .read_to_end(&mut data)?;
1179 Ok(data)
1180 }
1181}
1182
1183pub fn open_store(path: impl AsRef<Path>) -> io::Result<Rc<dyn Store>> {
1184 Ok(if let Ok(pst_file) = UnicodePstFile::open(path.as_ref()) {
1185 UnicodeStore::read(Rc::new(pst_file))?
1186 } else {
1187 let pst_file = AnsiPstFile::open(path.as_ref())?;
1188 AnsiStore::read(Rc::new(pst_file))?
1189 })
1190}