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"); }