public DataBlock GetDataBlock(int dataBlockIndex) { if (m_rootBlock == null) { throw new Exception("Data tree root block is null"); } else if (m_rootBlock is DataBlock) { if (dataBlockIndex == 0) { // We do not want to return the reference return((DataBlock)m_rootBlock.Clone()); } else { throw new ArgumentException("Data tree root block is a data block, data block index must be 0"); } } else if (m_rootBlock is XBlock) { XBlock rootBlock = (XBlock)m_rootBlock; if (dataBlockIndex < rootBlock.rgbid.Count) { BlockID blockID = rootBlock.rgbid[dataBlockIndex]; DataBlock block = (DataBlock)GetBlock(blockID.Value); return(block); } else { throw new ArgumentException("Invalid data block index"); } } else // XXBlock { // We assume that all XBlocks are completely filled except the last one. int xBlockIndex = dataBlockIndex / XBlock.MaximumNumberOfDataBlocks; int dataBlockIndexInXBlock = dataBlockIndex % XBlock.MaximumNumberOfDataBlocks; XXBlock rootBlock = (XXBlock)m_rootBlock; if (xBlockIndex < rootBlock.NumberOfXBlocks) { XBlock xBlock = (XBlock)GetBlock(rootBlock.rgbid[xBlockIndex]); if (dataBlockIndexInXBlock < xBlock.NumberOfDataBlocks) { BlockID blockID = xBlock.rgbid[dataBlockIndexInXBlock]; DataBlock block = (DataBlock)GetBlock(blockID.Value); return(block); } else { throw new ArgumentException("Invalid data block index"); } } else { throw new ArgumentException("Invalid XBlock index"); } } }
public override Block Clone() { XXBlock result = (XXBlock)MemberwiseClone(); result.rgbid = new List <BlockID>(); foreach (BlockID blockID in rgbid) { result.rgbid.Add(blockID.Clone()); } return(result); }
public bool IsDataBlockPendingWrite(int dataBlockIndex) { if (m_rootBlock is DataBlock) { return(IsBlockPendingWrite(m_rootBlock)); } else if (m_rootBlock is XBlock) { BlockID blockID = ((XBlock)m_rootBlock).rgbid[dataBlockIndex]; return(IsBlockPendingWrite(blockID)); } else // XXBlock { int xBlockIndex = dataBlockIndex / XBlock.MaximumNumberOfDataBlocks; int dataBlockIndexInXBlock = dataBlockIndex % XBlock.MaximumNumberOfDataBlocks; XXBlock rootBlock = (XXBlock)m_rootBlock; XBlock xBlock = (XBlock)GetBlock(rootBlock.rgbid[xBlockIndex]); BlockID blockID = xBlock.rgbid[dataBlockIndexInXBlock]; return(IsBlockPendingWrite(blockID)); } }
public void DeleteLastDataBlock() { if (m_rootBlock == null) { return; } else if (m_rootBlock is DataBlock) { DeleteBlock(m_rootBlock); m_rootBlock = null; } else if (m_rootBlock is XBlock) { XBlock rootBlock = (XBlock)m_rootBlock; int dataBlockIndex = rootBlock.rgbid.Count - 1; ulong currentBlockID = rootBlock.rgbid[dataBlockIndex].Value; DataBlock dataBlock = (DataBlock)GetBlock(currentBlockID); int currentDataLength = dataBlock.Data.Length; DeleteBlock(dataBlock); rootBlock.rgbid.RemoveAt(dataBlockIndex); // Update the total length uint totalLength = (uint)(rootBlock.lcbTotal - currentDataLength); rootBlock.lcbTotal = totalLength; UpdateBlock(rootBlock); } else // XXBlock { XXBlock rootBlock = (XXBlock)m_rootBlock; BlockID lastXBlockID = rootBlock.rgbid[rootBlock.NumberOfXBlocks - 1]; XBlock lastXBlock = (XBlock)GetBlock(lastXBlockID); if (lastXBlock.NumberOfDataBlocks > 1) { int dataBlockIndexInXBlock = lastXBlock.NumberOfDataBlocks - 1; ulong currentBlockID = lastXBlock.rgbid[dataBlockIndexInXBlock].Value; DataBlock dataBlock = (DataBlock)GetBlock(currentBlockID); int currentDataLength = dataBlock.Data.Length; DeleteBlock(dataBlock); lastXBlock.rgbid.RemoveAt(dataBlockIndexInXBlock); // Update the total length uint xBlockTotalLength = (uint)(lastXBlock.lcbTotal - currentDataLength); lastXBlock.lcbTotal = xBlockTotalLength; UpdateBlock(lastXBlock); uint totalLength = (uint)(rootBlock.lcbTotal - currentDataLength); rootBlock.lcbTotal = totalLength; UpdateBlock(rootBlock); } else if (lastXBlock.NumberOfDataBlocks == 1) { ulong currentBlockID = lastXBlock.rgbid[0].Value; DataBlock dataBlock = (DataBlock)GetBlock(currentBlockID); int currentDataLength = dataBlock.Data.Length; DeleteBlock(dataBlock); DeleteBlock(lastXBlock); int lastXBlockIndex = rootBlock.rgbid.Count - 1; rootBlock.rgbid.RemoveAt(lastXBlockIndex); rootBlock.lcbTotal = (uint)(rootBlock.lcbTotal - currentDataLength); UpdateBlock(rootBlock); } } }
public void AddDataBlock(byte[] blockData) { // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a5f9c653-40f5-4638-85d3-00c54607d984/ // We must completely fill the previous block in order for Outlook 2003 to read the next block // This is true for both the Subnode containing the rows of a Table Context // and for the DataTree containing the heap items of a Table Context. // Emphasis: The block that must be filled is the one preceding the block that was added. if (DataBlockCount > 0) { ZeroFillBlock(DataBlockCount - 1); } DataBlock block = new DataBlock(m_bCryptMethod); block.Data = blockData; AddBlock(block); // update the root if (m_rootBlock == null) { m_rootBlock = block; } else if (m_rootBlock is DataBlock) { DataBlock firstDataBlock = (DataBlock)m_rootBlock; // Create an XBlock: XBlock rootBlock = new XBlock(); rootBlock.rgbid.Add(firstDataBlock.BlockID); rootBlock.rgbid.Add(block.BlockID); rootBlock.lcbTotal = (uint)(firstDataBlock.Data.Length + blockData.Length); AddBlock(rootBlock); m_rootBlock = rootBlock; // Note: the reference from the node to the rootblock will be updated later } else if (m_rootBlock is XBlock) { XBlock rootBlock = (XBlock)m_rootBlock; if (rootBlock.NumberOfDataBlocks < XBlock.MaximumNumberOfDataBlocks) { rootBlock.rgbid.Add(block.BlockID); rootBlock.lcbTotal += (uint)blockData.Length; UpdateBlock(rootBlock); } else // We need to create a new XXBlock { // Create an XBlock: XBlock xBlock = new XBlock(); xBlock.rgbid.Add(block.BlockID); xBlock.lcbTotal = (uint)blockData.Length; AddBlock(xBlock); // Create an XXBlock XXBlock newRootBlock = new XXBlock(); newRootBlock.rgbid.Add(rootBlock.BlockID); newRootBlock.rgbid.Add(xBlock.BlockID); newRootBlock.lcbTotal = rootBlock.lcbTotal + xBlock.lcbTotal; AddBlock(newRootBlock); m_rootBlock = newRootBlock; // Note: the reference from the node to the rootblock will be updated later } } else // XXBlock { XXBlock rootBlock = (XXBlock)m_rootBlock; BlockID lastXBlockID = rootBlock.rgbid[rootBlock.NumberOfXBlocks - 1]; XBlock lastXBlock = (XBlock)GetBlock(lastXBlockID); if (lastXBlock.NumberOfDataBlocks < XBlock.MaximumNumberOfDataBlocks) { lastXBlock.rgbid.Add(block.BlockID); lastXBlock.lcbTotal += (uint)blockData.Length; UpdateBlock(lastXBlock); rootBlock.lcbTotal += (uint)blockData.Length; UpdateBlock(rootBlock); } else if (rootBlock.NumberOfXBlocks < XXBlock.MaximumNumberOfXBlocks) { // Create an XBlock: XBlock xBlock = new XBlock(); xBlock.rgbid.Add(block.BlockID); xBlock.lcbTotal = (uint)blockData.Length; AddBlock(xBlock); rootBlock.rgbid.Add(xBlock.BlockID); rootBlock.lcbTotal += (uint)blockData.Length; UpdateBlock(rootBlock); } else { throw new Exception("Data Tree is full"); } } }
public void UpdateDataBlock(int dataBlockIndex, byte[] blockData) { if (m_rootBlock == null) { throw new Exception("Data tree root block is null"); } else if (m_rootBlock is DataBlock) { if (dataBlockIndex == 0) { ((DataBlock)m_rootBlock).Data = blockData; UpdateBlock(m_rootBlock); } else { throw new ArgumentException("Data tree root block is a data block, data block index must be 0"); } } else if (m_rootBlock is XBlock) { XBlock rootBlock = (XBlock)m_rootBlock; if (dataBlockIndex < rootBlock.rgbid.Count) { ulong currentBlockID = rootBlock.rgbid[dataBlockIndex].Value; int currentDataLength = ((DataBlock)GetBlock(currentBlockID)).Data.Length; DataBlock block = new DataBlock(m_bCryptMethod); block.Data = blockData; block.BlockID = new BlockID(currentBlockID); UpdateBlock(block); if (block.BlockID.Value != currentBlockID) { // A new block has been written instead of the old one, // update the root block rootBlock.rgbid[dataBlockIndex] = block.BlockID; } // Update the total length uint totalLength = (uint)(rootBlock.lcbTotal + blockData.Length - currentDataLength); rootBlock.lcbTotal = totalLength; UpdateBlock(rootBlock); } else { throw new ArgumentException("Invalid data block index"); } } else // XXBlock { int xBlockIndex = dataBlockIndex / XBlock.MaximumNumberOfDataBlocks; int dataBlockIndexInXBlock = dataBlockIndex % XBlock.MaximumNumberOfDataBlocks; XXBlock rootBlock = (XXBlock)m_rootBlock; if (xBlockIndex < rootBlock.NumberOfXBlocks) { XBlock xBlock = (XBlock)GetBlock(rootBlock.rgbid[xBlockIndex]); if (dataBlockIndexInXBlock < xBlock.NumberOfDataBlocks) { ulong currentBlockID = xBlock.rgbid[dataBlockIndexInXBlock].Value; int currentDataLength = ((DataBlock)GetBlock(currentBlockID)).Data.Length; DataBlock block = new DataBlock(m_bCryptMethod); block.Data = blockData; block.BlockID = new BlockID(currentBlockID); UpdateBlock(block); if (block.BlockID.Value != currentBlockID) { // A new block has been written instead of the old one, // update the root block xBlock.rgbid[dataBlockIndexInXBlock] = block.BlockID; } // Update the total length uint xBlockTotalLength = (uint)(xBlock.lcbTotal + blockData.Length - currentDataLength); xBlock.lcbTotal = xBlockTotalLength; ulong currentXBlockID = rootBlock.rgbid[xBlockIndex].Value; UpdateBlock(xBlock); if (xBlock.BlockID.Value != currentXBlockID) { rootBlock.rgbid[xBlockIndex] = xBlock.BlockID; } uint totalLength = (uint)(rootBlock.lcbTotal + blockData.Length - currentDataLength); rootBlock.lcbTotal = totalLength; UpdateBlock(rootBlock); } else { throw new ArgumentException("Invalid data block index"); } } else { throw new ArgumentException("Invalid XBlock index"); } } }
public static Block ReadFromStream(Stream stream, BlockRef blockRef, int dataLength, bCryptMethodName bCryptMethod) { long offset = (long)blockRef.ib; int totalLength = GetTotalBlockLength(dataLength); stream.Seek(offset, SeekOrigin.Begin); byte[] buffer = new byte[totalLength]; stream.Read(buffer, 0, totalLength); BlockTrailer trailer = BlockTrailer.ReadFromEndOfBuffer(buffer); Block block; if (trailer.bid.Internal) { // XBlock or XXBlock byte btype = buffer[0]; byte cLevel = buffer[1]; if (btype == (byte)BlockType.XBlock && cLevel == 0x01) { // XBLOCK block = new XBlock(buffer); } else if (btype == (byte)BlockType.XXBlock && cLevel == 0x02) { // XXBLOCK block = new XXBlock(buffer); } else if (btype == (byte)BlockType.SLBLOCK && cLevel == 0x00) { // SLBLock block = new SubnodeLeafBlock(buffer); } else if (btype == (byte)BlockType.SIBLOCK && cLevel == 0x01) { // SIBLock block = new SubnodeIntermediateBlock(buffer); } else { throw new Exception("Internal block, but not XBLOCK, XXBlock, SLBLOCK or SIBLOCK"); } } else { block = new DataBlock(buffer, bCryptMethod); } // See question 3 at: // http://social.msdn.microsoft.com/Forums/en-CA/os_binaryfile/thread/923f5964-4a89-4811-86c2-06a553c34510 // However, so far all tests suggest that there should be no problem to use BlockID.Value for both arguments if (blockRef.bid.LookupValue != block.BlockID.LookupValue) { throw new InvalidBlockIDException(); } if (dataLength != trailer.cb) { throw new Exception("Invalid block length"); } uint crc = PSTCRCCalculation.ComputeCRC(buffer, dataLength); if (block.BlockTrailer.dwCRC != crc) { throw new InvalidChecksumException(); } uint signature = BlockTrailer.ComputeSignature(blockRef.ib, blockRef.bid.Value); if (block.BlockTrailer.wSig != signature) { throw new InvalidChecksumException(); } return(block); }