/// <summary> /// Disposes of underlying resources. /// </summary> /// <param name="disposing">Set to <c>true</c> if called within Dispose(), /// else <c>false</c>.</param> protected override void Dispose(bool disposing) { try { if (disposing) { if (_writeOccurred && _stream != null) { _header.ModificationId = Guid.NewGuid(); _stream.Position = PreHeaderRecord.Size; _header.Write(_stream); } if (_ownsStream == Ownership.Dispose && _stream != null) { _stream.Dispose(); _stream = null; } } } finally { base.Dispose(disposing); } }
/// <summary> /// Initializes a stream as a fixed-sized VDI file. /// </summary> /// <param name="stream">The stream to initialize.</param> /// <param name="ownsStream">Indicates if the new instance controls the lifetime of the stream.</param> /// <param name="capacity">The desired capacity of the new disk</param> /// <returns>An object that accesses the stream as a VDI file</returns> public static DiskImageFile InitializeFixed(Stream stream, Ownership ownsStream, long capacity) { PreHeaderRecord preHeader = PreHeaderRecord.Initialized(); HeaderRecord header = HeaderRecord.Initialized(ImageType.Fixed, ImageFlags.None, capacity, 1024 * 1024, 0); byte[] blockTable = new byte[header.BlockCount * 4]; for (int i = 0; i < header.BlockCount; ++i) { Utilities.WriteBytesLittleEndian((uint)i, blockTable, i * 4); } header.BlocksAllocated = header.BlockCount; stream.Position = 0; preHeader.Write(stream); header.Write(stream); stream.Position = header.BlocksOffset; stream.Write(blockTable, 0, blockTable.Length); long totalSize = header.DataOffset + ((long)header.BlockSize * (long)header.BlockCount); if (stream.Length < totalSize) { stream.SetLength(totalSize); } return(new DiskImageFile(stream, ownsStream)); }
/// <summary> /// Initializes a stream as a dynamically-sized VDI file. /// </summary> /// <param name="stream">The stream to initialize.</param> /// <param name="ownsStream">Indicates if the new instance controls the lifetime of the stream.</param> /// <param name="capacity">The desired capacity of the new disk</param> /// <returns>An object that accesses the stream as a VDI file</returns> public static DiskImageFile InitializeDynamic(Stream stream, Ownership ownsStream, long capacity) { PreHeaderRecord preHeader = PreHeaderRecord.Initialized(); HeaderRecord header = HeaderRecord.Initialized(ImageType.Dynamic, ImageFlags.None, capacity, 1024 * 1024, 0); byte[] blockTable = new byte[header.BlockCount * 4]; for (int i = 0; i < blockTable.Length; ++i) { blockTable[i] = 0xFF; } header.BlocksAllocated = 0; stream.Position = 0; preHeader.Write(stream); header.Write(stream); stream.Position = header.BlocksOffset; stream.Write(blockTable, 0, blockTable.Length); return(new DiskImageFile(stream, ownsStream)); }
public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); if (!CanWrite) { throw new IOException("Attempt to write to read-only stream"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", count, "Attempt to write negative number of bytes"); } if (_atEof || _position + count > _fileHeader.DiskSize) { _atEof = true; throw new IOException("Attempt to write beyond end of file"); } // On first write, notify event listeners - they just get to find out that some // write occurred, not about each write. if (!_writeNotified) { OnWriteOccurred(); _writeNotified = true; } int numWritten = 0; while (numWritten < count) { int block = (int)(_position / _fileHeader.BlockSize); int offsetInBlock = (int)(_position % _fileHeader.BlockSize); int toWrite = Math.Min(count - numWritten, _fileHeader.BlockSize - offsetInBlock); // Optimize away zero-writes if (_blockTable[block] == BlockZero || (_blockTable[block] == BlockFree && toWrite == _fileHeader.BlockSize)) { if (Utilities.IsAllZeros(buffer, offset + numWritten, toWrite)) { numWritten += toWrite; _position += toWrite; continue; } } if (_blockTable[block] == BlockFree || _blockTable[block] == BlockZero) { byte[] writeBuffer = buffer; int writeBufferOffset = offset + numWritten; if (toWrite != _fileHeader.BlockSize) { writeBuffer = new byte[_fileHeader.BlockSize]; if (_blockTable[block] == BlockFree) { // TODO: Use parent stream data... } // Copy actual data into temporary buffer, then this is a full block write. Array.Copy(buffer, offset + numWritten, writeBuffer, offsetInBlock, toWrite); writeBufferOffset = 0; } long blockOffset = ((long)_fileHeader.BlocksAllocated) * (_fileHeader.BlockSize + _fileHeader.BlockExtraSize); long filePos = ((long)_fileHeader.DataOffset) + _fileHeader.BlockExtraSize + blockOffset; _fileStream.Position = filePos; _fileStream.Write(writeBuffer, writeBufferOffset, _fileHeader.BlockSize); _blockTable[block] = (uint)_fileHeader.BlocksAllocated; // Update the file header on disk, to indicate where the next free block is _fileHeader.BlocksAllocated++; _fileStream.Position = PreHeaderRecord.Size; _fileHeader.Write(_fileStream); // Update the block table on disk, to indicate where this block is WriteBlockTableEntry(block); } else { // Existing block, simply overwrite the existing data long blockOffset = ((long)_blockTable[block]) * (_fileHeader.BlockSize + _fileHeader.BlockExtraSize); long filePos = ((long)_fileHeader.DataOffset) + _fileHeader.BlockExtraSize + blockOffset + offsetInBlock; _fileStream.Position = filePos; _fileStream.Write(buffer, offset + numWritten, toWrite); } numWritten += toWrite; _position += toWrite; } }