private static void TestCreateNewFile(DiskIo stream, FileHeaderBlock fat)
        {
            SubFileName id1 = SubFileName.CreateRandom();
            SubFileName id2 = SubFileName.CreateRandom();
            SubFileName id3 = SubFileName.CreateRandom();
            TransactionalEdit trans = new TransactionalEdit(stream);
            //create 3 files

            SubFileStream fs1 = trans.CreateFile(id1);
            SubFileStream fs2 = trans.CreateFile(id2);
            SubFileStream fs3 = trans.CreateFile(id3);
            if (fs1.SubFile.FileName != id1)
                throw new Exception();
            //write to the three files
            SubFileStreamTest.TestSingleByteWrite(fs1);
            SubFileStreamTest.TestCustomSizeWrite(fs2, 5);
            SubFileStreamTest.TestCustomSizeWrite(fs3, BlockDataLength + 20);

            //read from them and verify content.
            SubFileStreamTest.TestCustomSizeRead(fs3, BlockDataLength + 20);
            SubFileStreamTest.TestCustomSizeRead(fs2, 5);
            SubFileStreamTest.TestSingleByteRead(fs1);

            fs1.Dispose();
            fs2.Dispose();
            fs3.Dispose();

            trans.CommitAndDispose();
        }
        /// <summary>
        /// Creates an SubFileStream
        /// </summary>
        /// <param name="dataReader">The location to read from.</param>
        /// <param name="subFile">The file to read.</param>
        /// <param name="fileHeaderBlock">The FileAllocationTable</param>
        /// <param name="isReadOnly">Determines if the stream allows editing.</param>
        internal SubFileStream(DiskIo dataReader, SubFileHeader subFile, FileHeaderBlock fileHeaderBlock, bool isReadOnly)
        {
            if (dataReader == null)
                throw new ArgumentNullException("dataReader");
            if (subFile == null)
                throw new ArgumentNullException("subFile");
            if (fileHeaderBlock == null)
                throw new ArgumentNullException("subFile");

            if (!isReadOnly)
            {
                if (dataReader.IsReadOnly)
                    throw new ArgumentException("This parameter cannot be read only when opening for writing", "dataReader");
                if (fileHeaderBlock.IsReadOnly)
                    throw new ArgumentException("This parameter cannot be read only when opening for writing", "fileHeaderBlock");
                if (subFile.IsReadOnly)
                    throw new ArgumentException("This parameter cannot be read only when opening for writing", "subFile");
            }
            if (isReadOnly)
            {
                if (!fileHeaderBlock.IsReadOnly)
                    throw new ArgumentException("This parameter must be read only when opening for reading", "fileHeaderBlock");
                if (!subFile.IsReadOnly)
                    throw new ArgumentException("This parameter must be read only when opening for reading", "subFile");
            }

            m_blockSize = dataReader.BlockSize;
            m_dataReader = dataReader;
            m_subFile = subFile;
            m_fileHeaderBlock = fileHeaderBlock;
            m_isReadOnly = isReadOnly;
        }
 /// <summary>
 /// Creates a readonly copy of a transaction.
 /// </summary>
 /// <param name="dataReader"></param>
 internal ReadSnapshot(DiskIo dataReader)
 {
     if (dataReader == null)
         throw new ArgumentNullException("dataReader");
     m_fileHeaderBlock = dataReader.LastCommittedHeader;
     m_dataReader = dataReader;
 }
 /// <summary>
 /// Creates a simplified file writer.
 /// </summary>
 /// <param name="pendingFileName"></param>
 /// <param name="completeFileName"></param>
 /// <param name="blockSize"></param>
 /// <param name="flags"></param>
 public SimplifiedFileWriter(string pendingFileName, string completeFileName, int blockSize, params Guid[] flags)
 {
     m_pendingFileName = pendingFileName;
     m_completeFileName = completeFileName;
     m_fileHeaderBlock = FileHeaderBlock.CreateNewSimplified(blockSize, flags).CloneEditable();
     m_fileHeaderBlock.ArchiveType = SortedTreeFile.FileType;
     m_stream = new FileStream(pendingFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None);
 }
 /// <summary>
 /// Creates a <see cref="ShadowCopyAllocator"/> that is used make shadow copies of blocks.
 /// </summary>
 /// <param name="ioSessions"></param>
 public ShadowCopyAllocator(SubFileDiskIoSessionPool ioSessions)
     : base(ioSessions)
 {
     if (ioSessions == null)
         throw new ArgumentNullException("ioSessions");
     if (ioSessions.IsReadOnly)
         throw new ArgumentException("DataReader is read only", "ioSessions");
     m_lastReadOnlyBlock = ioSessions.LastReadonlyBlock;
     m_fileHeaderBlock = ioSessions.Header;
     m_subFileHeader = ioSessions.File;
     m_ioSessions = ioSessions;
 }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <remarks>A debug function</remarks>
        internal static bool AreEqual(FileHeaderBlock other, FileHeaderBlock self)
        {
            if (other == null)
                return false;
            if (self == null)
                return false;
            if (self.CanWrite != other.CanWrite) return false;
            if (self.CanRead != other.CanRead) return false;
            if (self.ArchiveId != other.ArchiveId) return false;
            if (self.ArchiveType != other.ArchiveType) return false;
            if (self.SnapshotSequenceNumber != other.SnapshotSequenceNumber) return false;
            if (self.LastAllocatedBlock != other.LastAllocatedBlock) return false;
            if (self.FileCount != other.FileCount) return false;

            //compare files.
            if (self.Files == null)
            {
                if (other.Files != null) return false;
            }
            else
            {
                if (other.Files == null) return false;
                if (self.Files.Count != other.Files.Count) return false;
                for (int x = 0; x < self.Files.Count; x++)
                {
                    SubFileHeader subFile = self.Files[x];
                    SubFileHeader subFileOther = other.Files[x];

                    if (subFile == null)
                    {
                        if (subFileOther != null) return false;
                    }
                    else
                    {
                        if (subFileOther == null) return false;
                        if (!SubFileMetaDataTest.AreEqual(subFile, subFileOther)) return false;
                    }
                }
            }

            return (self.GetBytes().SequenceEqual(other.GetBytes()));
        }
        public SimplifiedSubFileStreamIoSession(FileStream stream, SubFileHeader subFile, FileHeaderBlock header)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            if (subFile == null)
                throw new ArgumentNullException("subFile");
            if (header == null)
                throw new ArgumentNullException("header");
            if (subFile.DirectBlock == 0)
                throw new Exception("Must assign subFile.DirectBlock");

            m_stream = stream;
            m_header = header;
            m_blockSize = header.BlockSize;
            m_subFile = subFile;
            m_memory = new Memory(m_blockSize);
            m_buffer = new byte[m_blockSize];
            m_currentPhysicalBlock = -1;
            m_blockDataLength = m_blockSize - FileStructureConstants.BlockFooterLength;
        }
        /// <summary>
        /// Creates an SimplifiedSubFileStream
        /// </summary>
        /// <param name="stream">The location to read from.</param>
        /// <param name="subFile">The file to read.</param>
        /// <param name="fileHeaderBlock">The FileAllocationTable</param>
        internal SimplifiedSubFileStream(FileStream stream, SubFileHeader subFile, FileHeaderBlock fileHeaderBlock)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            if (subFile == null)
                throw new ArgumentNullException("subFile");
            if (fileHeaderBlock == null)
                throw new ArgumentNullException("fileHeaderBlock");
            if (subFile.DirectBlock == 0)
                throw new Exception("Must assign subFile.DirectBlock");

            if (fileHeaderBlock.IsReadOnly)
                throw new ArgumentException("This parameter cannot be read only when opening for writing", "fileHeaderBlock");
            if (subFile.IsReadOnly)
                throw new ArgumentException("This parameter cannot be read only when opening for writing", "subFile");

            m_blockSize = fileHeaderBlock.BlockSize;
            m_stream = stream;
            m_subFile = subFile;
            m_fileHeaderBlock = fileHeaderBlock;
        }
        /// <summary>
        /// Creates a new DiskIoSession that can be used to read from the disk subsystem.
        /// </summary>
        /// <param name="diskIo">owner of the disk</param>
        /// <param name="ioSession">the base ioSession to use for this io session</param>
        /// <param name="file">The file that will be read from this diskIoSession</param>
        public DiskIoSession(DiskIo diskIo, BinaryStreamIoSessionBase ioSession, FileHeaderBlock header, SubFileHeader file)
        {
            if (diskIo == null)
                throw new ArgumentNullException("diskIo");
            if (diskIo.IsDisposed)
                throw new ObjectDisposedException(diskIo.GetType().FullName);
            if (ioSession == null)
                throw new ArgumentNullException("ioSession");
            if (file == null)
                throw new ArgumentNullException("file");

            m_args = new BlockArguments();
            m_lastReadonlyBlock = diskIo.LastReadonlyBlock;
            m_diskMediumIoSession = ioSession;
            m_snapshotSequenceNumber = header.SnapshotSequenceNumber;
            m_fileIdNumber = file.FileIdNumber;
            m_isReadOnly = file.IsReadOnly | diskIo.IsReadOnly;
            m_blockSize = diskIo.BlockSize;
            m_diskIo = diskIo;
            IsValid = false;
            IsDisposed = false;
        }
 /// <summary>
 /// Creates this file with the following data.
 /// </summary>
 /// <param name="diskIo"></param>
 /// <param name="header"></param>
 /// <param name="file"></param>
 /// <param name="isReadOnly"></param>
 public SubFileDiskIoSessionPool(DiskIo diskIo, FileHeaderBlock header, SubFileHeader file, bool isReadOnly)
 {
     LastReadonlyBlock = diskIo.LastCommittedHeader.LastAllocatedBlock;
     File = file;
     Header = header;
     IsReadOnly = isReadOnly;
     SourceData = diskIo.CreateDiskIoSession(header, file);
     SourceIndex = diskIo.CreateDiskIoSession(header, file);
     if (!isReadOnly)
     {
         DestinationData = diskIo.CreateDiskIoSession(header, file);
         DestinationIndex = diskIo.CreateDiskIoSession(header, file);
     }
 }
        private static void TestOpenExistingFile(DiskIo stream, FileHeaderBlock fat)
        {
            Guid id = Guid.NewGuid();
            TransactionalEdit trans = new TransactionalEdit(stream);
            //create 3 files

            SubFileStream fs1 = trans.OpenFile(0);
            SubFileStream fs2 = trans.OpenFile(1);
            SubFileStream fs3 = trans.OpenFile(2);

            //read from them and verify content.
            SubFileStreamTest.TestSingleByteRead(fs1);
            SubFileStreamTest.TestCustomSizeRead(fs2, 5);
            SubFileStreamTest.TestCustomSizeRead(fs3, BlockDataLength + 20);

            //rewrite bad data.
            SubFileStreamTest.TestSingleByteWrite(fs2);
            SubFileStreamTest.TestCustomSizeWrite(fs3, 5);
            SubFileStreamTest.TestCustomSizeWrite(fs1, BlockDataLength + 20);

            fs1.Dispose();
            fs2.Dispose();
            fs3.Dispose();

            trans.CommitAndDispose();
        }
        /// <summary>
        /// Creates a file backed memory stream.
        /// </summary>
        /// <param name="stream">The <see cref="CustomFileStream"/> to buffer</param>
        /// <param name="pool">The <see cref="MemoryPool"/> to allocate memory from</param>
        /// <param name="header">The <see cref="FileHeaderBlock"/> to be managed when modifications occur</param>
        /// <param name="isNewFile">Tells if this is a newly created file. This will make sure that the 
        /// first 10 pages have the header data copied to it.</param>
        public BufferedFile(CustomFileStream stream, MemoryPool pool, FileHeaderBlock header, bool isNewFile)
        {
            m_fileStructureBlockSize = header.BlockSize;
            m_diskBlockSize = pool.PageSize;
            m_lengthOfHeader = header.BlockSize * header.HeaderBlockCount;
            m_writeBuffer = new MemoryPoolStreamCore(pool);
            m_pool = pool;
            m_queue = stream;
            m_syncRoot = new object();
            m_pageReplacementAlgorithm = new PageReplacementAlgorithm(pool);
            pool.RequestCollection += m_pool_RequestCollection;

            if (isNewFile)
            {
                byte[] headerBytes = header.GetBytes();
                for (int x = 0; x < header.HeaderBlockCount; x++)
                {
                    m_queue.WriteRaw(0, headerBytes, headerBytes.Length);
                }
            }
            m_lengthOfCommittedData = (header.LastAllocatedBlock + 1) * (long)header.BlockSize;
            m_writeBuffer.ConfigureAlignment(m_lengthOfCommittedData, pool.PageSize);
        }
        private static void TestVerifyRollback(DiskIo stream, FileHeaderBlock fat)
        {
            Guid id = Guid.NewGuid();
            TransactionalEdit trans = new TransactionalEdit(stream);

            if (trans.Files.Count != 3)
                throw new Exception();

            //open files
            SubFileStream fs1 = trans.OpenFile(0);
            SubFileStream fs2 = trans.OpenFile(1);
            SubFileStream fs3 = trans.OpenFile(2);

            //read from them and verify content.
            SubFileStreamTest.TestSingleByteRead(fs2);
            SubFileStreamTest.TestCustomSizeRead(fs3, 5);
            SubFileStreamTest.TestCustomSizeRead(fs1, BlockDataLength + 20);

            fs1.Dispose();
            fs2.Dispose();
            fs3.Dispose();

            trans.Dispose();
        }
        private static void TestRollback(DiskIo stream, FileHeaderBlock fat)
        {
            SubFileName id1 = SubFileName.CreateRandom();
            SubFileName id2 = SubFileName.CreateRandom();
            SubFileName id3 = SubFileName.CreateRandom();
            TransactionalEdit trans = new TransactionalEdit(stream);

            //create 3 files additional files
            SubFileStream fs21 = trans.CreateFile(id1);
            SubFileStream fs22 = trans.CreateFile(id2);
            SubFileStream fs23 = trans.CreateFile(id3);

            //open files
            SubFileStream fs1 = trans.OpenFile(0);
            SubFileStream fs2 = trans.OpenFile(1);
            SubFileStream fs3 = trans.OpenFile(2);

            //read from them and verify content.
            SubFileStreamTest.TestSingleByteRead(fs2);
            SubFileStreamTest.TestCustomSizeRead(fs3, 5);
            SubFileStreamTest.TestCustomSizeRead(fs1, BlockDataLength + 20);

            //rewrite bad data.
            SubFileStreamTest.TestSingleByteWrite(fs3);
            SubFileStreamTest.TestCustomSizeWrite(fs1, 5);
            SubFileStreamTest.TestCustomSizeWrite(fs2, BlockDataLength + 20);

            fs1.Dispose();
            fs2.Dispose();
            fs3.Dispose();

            fs21.Dispose();
            fs22.Dispose();
            fs23.Dispose();

            trans.RollbackAndDispose();
        }
        /// <summary>
        /// Executes a commit of data. This will flush the data to the disk use the provided header data to properly
        /// execute this function.
        /// </summary>
        /// <param name="header"></param>
        public void CommitChanges(FileHeaderBlock header)
        {
            using (var pageLock = new IoSession(this, m_pageReplacementAlgorithm))
            {
                //Determine how much committed data to write
                long lengthOfAllData = (header.LastAllocatedBlock + 1) * (long)m_fileStructureBlockSize;
                long copyLength = lengthOfAllData - m_lengthOfCommittedData;

                //Write the uncommitted data.
                m_queue.Write(m_lengthOfCommittedData, m_writeBuffer, copyLength, waitForWriteToDisk: true);

                byte[] bytes = header.GetBytes();
                if (header.HeaderBlockCount == 10)
                {
                    //Update the new header to position 0, position 1, and one of position 2-9
                    m_queue.WriteRaw(0, bytes, m_fileStructureBlockSize);
                    m_queue.WriteRaw(m_fileStructureBlockSize, bytes, m_fileStructureBlockSize);
                    m_queue.WriteRaw(m_fileStructureBlockSize * ((header.SnapshotSequenceNumber & 7) + 2), bytes, m_fileStructureBlockSize);
                }
                else
                {
                    for (int x = 0; x < header.HeaderBlockCount; x++)
                    {
                        m_queue.WriteRaw(x * m_fileStructureBlockSize, bytes, m_fileStructureBlockSize);
                    }
                }

                m_queue.FlushFileBuffers();

                long startPos;

                //Copy recently committed data to the buffer pool
                if ((m_lengthOfCommittedData & (m_diskBlockSize - 1)) != 0) //Only if there is a split page.
                {
                    startPos = m_lengthOfCommittedData & (~(long)(m_diskBlockSize - 1));
                    //Finish filling up the split page in the buffer.
                    IntPtr ptrDest;

                    if (pageLock.TryGetSubPage(startPos, out ptrDest))
                    {
                        int length;
                        IntPtr ptrSrc;
                        m_writeBuffer.ReadBlock(m_lengthOfCommittedData, out ptrSrc, out length);
                        Footer.WriteChecksumResultsToFooter(ptrSrc, m_fileStructureBlockSize, length);
                        ptrDest += (m_diskBlockSize - length);
                        Memory.Copy(ptrSrc, ptrDest, length);
                    }
                    startPos += m_diskBlockSize;
                }
                else
                {
                    startPos = m_lengthOfCommittedData;
                }

                while (startPos < lengthOfAllData)
                {
                    //If the address doesn't exist in the current list. Read it from the disk.
                    int poolPageIndex;
                    IntPtr poolAddress;
                    m_pool.AllocatePage(out poolPageIndex, out poolAddress);
                    m_writeBuffer.CopyTo(startPos, poolAddress, m_diskBlockSize);
                    Footer.WriteChecksumResultsToFooter(poolAddress, m_fileStructureBlockSize, m_diskBlockSize);

                    if (!m_pageReplacementAlgorithm.TryAddPage(startPos, poolAddress, poolPageIndex))
                        m_pool.ReleasePage(poolPageIndex);

                    startPos += m_diskBlockSize;
                }
                m_lengthOfCommittedData = lengthOfAllData;
            }
            ReleaseWriteBufferSpace();
        }
 private static void CheckEqual(FileHeaderBlock RO, FileHeaderBlock RW)
 {
     if (!AreEqual(RW, RO))
         throw new Exception();
 }
 /// <summary>
 /// Occurs when committing the following data to the disk.
 /// This will copy any pending data to the disk in a manner that
 /// will protect against corruption.
 /// </summary>
 /// <param name="header"></param>
 public void CommitChanges(FileHeaderBlock header)
 {
     if (m_disposed)
         throw new ObjectDisposedException(GetType().FullName);
     if (m_isReadOnly)
         throw new ReadOnlyException();
     m_stream.CommitChanges(header);
 }
 /// <summary>
 /// Occurs when committing the following data to the disk.
 /// This will copy any pending data to the disk in a manner that
 /// will protect against corruption.
 /// </summary>
 /// <param name="header"></param>
 public void CommitChanges(FileHeaderBlock header)
 {
     header.IsReadOnly = true;
     m_disk.CommitChanges(header);
     Thread.MemoryBarrier();
     m_header = header;
 }
 /// <summary>
 /// Creates a <see cref="DiskIoSession"/> that can be used to perform basic read/write functions.
 /// </summary>
 /// <returns></returns>
 public DiskIoSession CreateDiskIoSession(FileHeaderBlock header, SubFileHeader file)
 {
     if (m_disposed)
         throw new ObjectDisposedException(GetType().FullName);
     return new DiskIoSession(this, m_stream.CreateIoSession(), header, file);
 }
 /// <summary>
 /// Class is created through static methods of this class.
 /// </summary>
 /// <param name="disk">the underlying disk medium</param>
 /// <param name="header">the header data to use.</param>
 private DiskMedium(IDiskMediumCoreFunctions disk, FileHeaderBlock header)
 {
     m_header = header;
     m_disk = disk;
     m_blockSize = header.BlockSize;
 }
 /// <summary>
 /// Executes a commit of data. This will flush the data to the disk use the provided header data to properly
 /// execute this function.
 /// </summary>
 /// <param name="headerBlock"></param>
 public void CommitChanges(FileHeaderBlock headerBlock)
 {
     if (IsDisposed)
         throw new ObjectDisposedException("MemoryStream");
 }