Example #1
0
        /// <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);
            }
        }
Example #2
0
        /// <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 + header.BlockSize * (long)header.BlockCount;

            if (stream.Length < totalSize)
            {
                stream.SetLength(totalSize);
            }

            return(new DiskImageFile(stream, ownsStream));
        }
Example #3
0
        /// <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));
        }
Example #4
0
        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(nameof(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 = _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 = _blockTable[block] * (_fileHeader.BlockSize + _fileHeader.BlockExtraSize);
                    long filePos     = _fileHeader.DataOffset + _fileHeader.BlockExtraSize + blockOffset +
                                       offsetInBlock;
                    _fileStream.Position = filePos;
                    _fileStream.Write(buffer, offset + numWritten, toWrite);
                }

                numWritten += toWrite;
                _position  += toWrite;
            }
        }