Example #1
0
        public BlockBTreeEntry FindBlockEntryByBlockID(ulong blockID)
        {
            // Readers MUST ignore the reserved bit and treat it as zero before looking up the BID from the BBT
            //
            // Documented cases where the Reserved bit was set to true:
            // 1. PST was created by Outlook 2010 RTM, the hirerarchy table of the 'Top of Personal Folders'
            //    had a rows subnode, and the entry in the subnode-BTree had the Reserved bit set to true,
            //    However, both the BBT entry (BREF) and the PageTrailer had the corresponding entry with the bit set to false.
            // 2. PST was created by Outlook 2007 RTM, this library created a calendar folder that was modified by Outlook (columns were added)
            //    The XBlock entry had the reserved bit set to true,
            //    However, both the BBT entry (BREF) and the PageTrailer had the corresponding entry with the bit set to false.
            ulong lookupValue = blockID & 0xFFFFFFFFFFFFFFFEU;

            BlockBTreeLeafPage page = (BlockBTreeLeafPage)FindLeafBTreePage(lookupValue);

            if (page != null)
            {
                // page.cLevel is now 0
                List <BlockBTreeEntry> blockBTreeEntryList = page.BlockEntryList;
                foreach (BlockBTreeEntry entry in blockBTreeEntryList)
                {
                    if (entry.BREF.bid.Value == lookupValue)
                    {
                        return(entry);
                    }
                }
            }
            return(null);
        }
Example #2
0
        /// <summary>
        /// Since the PST is immutable, any change will result in a new BBT root page
        /// Note: we may leave an empty leaf page
        /// </summary>
        /// <returns>New BlockBTree root page</returns>
        public void DeleteBlockEntry(BlockID blockID)
        {
            BlockBTreeLeafPage leaf = (BlockBTreeLeafPage)FindLeafBTreePage(blockID.LookupValue);

            // Remove the entry
            int index = leaf.RemoveEntry(blockID.LookupValue);

            if (index == 0)
            {
                // scanpst.exe report an error if page key does not match the first entry,
                // so we want to update the parent
                if (leaf.ParentPage != null)
                {
                    if (leaf.BlockEntryList.Count > 0)
                    {
                        UpdateIndexEntry(leaf.ParentPage, leaf.BlockID, leaf.PageKey);
                    }
                    else
                    {
                        DeleteIndexEntry(leaf.ParentPage, leaf.BlockID);
                        DeletePage(leaf);
                        return;
                    }
                }
            }

            // now we have to store the new leaf page, and cascade the changes up to the root page
            UpdatePageAndReferences(leaf);
        }
Example #3
0
        public static BTreePage ReadFromStream(Stream stream, BlockRef blockRef)
        {
            long offset = (long)blockRef.ib;

            stream.Seek(offset, SeekOrigin.Begin);
            byte[] buffer = new byte[Length];
            stream.Read(buffer, 0, Length);
            PageTypeName ptype = (PageTypeName)buffer[PageTrailer.OffsetFromPageStart + 0];
            BTreePage    page;
            byte         cLevel = buffer[491];

            if (cLevel > 0)
            {
                // If cLevel is greater than 0, then each entry in the array is of type BTENTRY.
                page = new BTreeIndexPage(buffer);
            }
            else
            {
                // If cLevel is 0, then each entry is either of type BBTENTRY or NBTENTRY, depending on the ptype of the page.
                if (ptype == PageTypeName.ptypeBBT)
                {
                    page = new BlockBTreeLeafPage(buffer);
                }
                else if (ptype == PageTypeName.ptypeNBT)
                {
                    page = new NodeBTreeLeafPage(buffer);
                }
                else
                {
                    throw new ArgumentException("BTreePage has incorrect ptype");
                }
            }
            page.Offset = (ulong)blockRef.ib;
            if (blockRef.bid.Value != page.BlockID.Value)
            {
                throw new InvalidBlockIDException();
            }
            uint crc = PSTCRCCalculation.ComputeCRC(buffer, PageTrailer.OffsetFromPageStart);

            if (page.pageTrailer.dwCRC != crc)
            {
                throw new InvalidChecksumException();
            }

            uint signature = BlockTrailer.ComputeSignature(blockRef.ib, blockRef.bid.Value);

            if (page.pageTrailer.wSig != signature)
            {
                throw new InvalidChecksumException();
            }
            return(page);
        }
Example #4
0
        public void UpdateBlockEntry(BlockID blockID, ushort cRef)
        {
            BlockBTreeLeafPage leaf = (BlockBTreeLeafPage)FindLeafBTreePage(blockID.LookupValue);
            int index = leaf.GetIndexOfEntry(blockID.LookupValue);

            if (index >= 0)
            {
                leaf.BlockEntryList[index].cRef = cRef;

                // now we have to store the new leaf page, and cascade the changes up to the root page
                UpdatePageAndReferences(leaf);
            }
        }
Example #5
0
        public void InsertBlockEntry(BlockBTreeEntry entryToInsert)
        {
            ulong key = entryToInsert.BREF.bid.Value;
            BlockBTreeLeafPage page = (BlockBTreeLeafPage)FindLeafBTreePage(key);

            if (page.BlockEntryList.Count < BlockBTreeLeafPage.MaximumNumberOfEntries)
            {
                int insertIndex = page.InsertSorted(entryToInsert);
                UpdatePageAndReferences(page);

                if (insertIndex == 0 && page.ParentPage != null)
                {
                    // page key has been modified, we must update the parent
                    UpdateIndexEntry(page.ParentPage, page.BlockID, page.PageKey);
                }
            }
            else
            {
                // We have to split the tree node
                BlockBTreeLeafPage newPage = page.Split();
                if (newPage.PageKey < key)
                {
                    newPage.InsertSorted(entryToInsert);
                }
                else
                {
                    int insertIndex = page.InsertSorted(entryToInsert);
                    if (insertIndex == 0 && page.ParentPage != null)
                    {
                        // page key has been modified, we must update the parent
                        UpdateIndexEntry(page.ParentPage, page.BlockID, page.PageKey);
                    }
                }
                UpdatePageAndReferences(page);
                AddPage(newPage);

                if (page.ParentPage == null)
                {
                    // this is a root page and it's full, we have to create a new root
                    CreateNewRoot();
                }

                InsertIndexEntry(page.ParentPage, newPage.PageKey, newPage.BlockID);
            }
        }
Example #6
0
        public BlockBTreeLeafPage Split()
        {
            int newNodeStartIndex      = BlockEntryList.Count / 2;
            BlockBTreeLeafPage newPage = new BlockBTreeLeafPage();

            // BlockID will be given when the page will be added
            newPage.cEntMax           = cEntMax;
            newPage.cbEnt             = cbEnt;
            newPage.cLevel            = cLevel;
            newPage.pageTrailer.ptype = pageTrailer.ptype;
            for (int index = newNodeStartIndex; index < BlockEntryList.Count; index++)
            {
                newPage.BlockEntryList.Add(BlockEntryList[index]);
            }

            BlockEntryList.RemoveRange(newNodeStartIndex, BlockEntryList.Count - newNodeStartIndex);
            return(newPage);
        }