mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-19 22:00:12 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Ntfs
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Compression;
|
||||
|
||||
internal sealed class CompressedClusterStream : ClusterStream
|
||||
{
|
||||
private INtfsContext _context;
|
||||
private NtfsAttribute _attr;
|
||||
private RawClusterStream _rawStream;
|
||||
private int _bytesPerCluster;
|
||||
|
||||
private byte[] _cacheBuffer;
|
||||
private long _cacheBufferVcn = -1;
|
||||
private byte[] _ioBuffer;
|
||||
|
||||
public CompressedClusterStream(INtfsContext context, NtfsAttribute attr, RawClusterStream rawStream)
|
||||
{
|
||||
_context = context;
|
||||
_attr = attr;
|
||||
_rawStream = rawStream;
|
||||
_bytesPerCluster = _context.BiosParameterBlock.BytesPerCluster;
|
||||
|
||||
_cacheBuffer = new byte[_attr.CompressionUnitSize * context.BiosParameterBlock.BytesPerCluster];
|
||||
_ioBuffer = new byte[_attr.CompressionUnitSize * context.BiosParameterBlock.BytesPerCluster];
|
||||
}
|
||||
|
||||
public override long AllocatedClusterCount
|
||||
{
|
||||
get { return _rawStream.AllocatedClusterCount; }
|
||||
}
|
||||
|
||||
public override IEnumerable<Range<long, long>> StoredClusters
|
||||
{
|
||||
get
|
||||
{
|
||||
return Range<long, long>.Chunked(_rawStream.StoredClusters, _attr.CompressionUnitSize);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsClusterStored(long vcn)
|
||||
{
|
||||
return _rawStream.IsClusterStored(CompressionStart(vcn));
|
||||
}
|
||||
|
||||
public override void ExpandToClusters(long numVirtualClusters, NonResidentAttributeRecord extent, bool allocate)
|
||||
{
|
||||
_rawStream.ExpandToClusters(Utilities.RoundUp(numVirtualClusters, _attr.CompressionUnitSize), extent, false);
|
||||
}
|
||||
|
||||
public override void TruncateToClusters(long numVirtualClusters)
|
||||
{
|
||||
long alignedNum = Utilities.RoundUp(numVirtualClusters, _attr.CompressionUnitSize);
|
||||
_rawStream.TruncateToClusters(alignedNum);
|
||||
if (alignedNum != numVirtualClusters)
|
||||
{
|
||||
_rawStream.ReleaseClusters(numVirtualClusters, (int)(alignedNum - numVirtualClusters));
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadClusters(long startVcn, int count, byte[] buffer, int offset)
|
||||
{
|
||||
if (buffer.Length < (count * _bytesPerCluster) + offset)
|
||||
{
|
||||
throw new ArgumentException("Cluster buffer too small", "buffer");
|
||||
}
|
||||
|
||||
int totalRead = 0;
|
||||
while (totalRead < count)
|
||||
{
|
||||
long focusVcn = startVcn + totalRead;
|
||||
LoadCache(focusVcn);
|
||||
|
||||
int cacheOffset = (int)(focusVcn - _cacheBufferVcn);
|
||||
int toCopy = Math.Min(_attr.CompressionUnitSize - cacheOffset, count - totalRead);
|
||||
|
||||
Array.Copy(_cacheBuffer, cacheOffset * _bytesPerCluster, buffer, offset + (totalRead * _bytesPerCluster), toCopy * _bytesPerCluster);
|
||||
|
||||
totalRead += toCopy;
|
||||
}
|
||||
}
|
||||
|
||||
public override int WriteClusters(long startVcn, int count, byte[] buffer, int offset)
|
||||
{
|
||||
if (buffer.Length < (count * _bytesPerCluster) + offset)
|
||||
{
|
||||
throw new ArgumentException("Cluster buffer too small", "buffer");
|
||||
}
|
||||
|
||||
int totalAllocated = 0;
|
||||
|
||||
int totalWritten = 0;
|
||||
while (totalWritten < count)
|
||||
{
|
||||
long focusVcn = startVcn + totalWritten;
|
||||
long cuStart = CompressionStart(focusVcn);
|
||||
|
||||
if (cuStart == focusVcn && count - totalWritten >= _attr.CompressionUnitSize)
|
||||
{
|
||||
// Aligned write...
|
||||
totalAllocated += CompressAndWriteClusters(focusVcn, _attr.CompressionUnitSize, buffer, offset + (totalWritten * _bytesPerCluster));
|
||||
|
||||
totalWritten += _attr.CompressionUnitSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unaligned, so go through cache
|
||||
LoadCache(focusVcn);
|
||||
|
||||
int cacheOffset = (int)(focusVcn - _cacheBufferVcn);
|
||||
int toCopy = Math.Min(count - totalWritten, _attr.CompressionUnitSize - cacheOffset);
|
||||
|
||||
Array.Copy(buffer, offset + (totalWritten * _bytesPerCluster), _cacheBuffer, cacheOffset * _bytesPerCluster, toCopy * _bytesPerCluster);
|
||||
|
||||
totalAllocated += CompressAndWriteClusters(_cacheBufferVcn, _attr.CompressionUnitSize, _cacheBuffer, 0);
|
||||
|
||||
totalWritten += toCopy;
|
||||
}
|
||||
}
|
||||
|
||||
return totalAllocated;
|
||||
}
|
||||
|
||||
public override int ClearClusters(long startVcn, int count)
|
||||
{
|
||||
int totalReleased = 0;
|
||||
int totalCleared = 0;
|
||||
while (totalCleared < count)
|
||||
{
|
||||
long focusVcn = startVcn + totalCleared;
|
||||
if (CompressionStart(focusVcn) == focusVcn && count - totalCleared >= _attr.CompressionUnitSize)
|
||||
{
|
||||
// Aligned - so it's a sparse compression unit...
|
||||
totalReleased += _rawStream.ReleaseClusters(startVcn, _attr.CompressionUnitSize);
|
||||
totalCleared += _attr.CompressionUnitSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
int toZero = (int)Math.Min(count - totalCleared, _attr.CompressionUnitSize - (focusVcn - CompressionStart(focusVcn)));
|
||||
totalReleased -= WriteZeroClusters(focusVcn, toZero);
|
||||
totalCleared += toZero;
|
||||
}
|
||||
}
|
||||
|
||||
return totalReleased;
|
||||
}
|
||||
|
||||
private int WriteZeroClusters(long focusVcn, int count)
|
||||
{
|
||||
int allocatedClusters = 0;
|
||||
|
||||
byte[] zeroBuffer = new byte[16 * _bytesPerCluster];
|
||||
int numWritten = 0;
|
||||
while (numWritten < count)
|
||||
{
|
||||
int toWrite = Math.Min(count - numWritten, 16);
|
||||
|
||||
allocatedClusters += WriteClusters(focusVcn + numWritten, toWrite, zeroBuffer, 0);
|
||||
|
||||
numWritten += toWrite;
|
||||
}
|
||||
|
||||
return allocatedClusters;
|
||||
}
|
||||
|
||||
private int CompressAndWriteClusters(long focusVcn, int count, byte[] buffer, int offset)
|
||||
{
|
||||
BlockCompressor compressor = _context.Options.Compressor;
|
||||
compressor.BlockSize = _bytesPerCluster;
|
||||
|
||||
int totalAllocated = 0;
|
||||
|
||||
int compressedLength = _ioBuffer.Length;
|
||||
var result = compressor.Compress(buffer, offset, _attr.CompressionUnitSize * _bytesPerCluster, _ioBuffer, 0, ref compressedLength);
|
||||
if (result == CompressionResult.AllZeros)
|
||||
{
|
||||
totalAllocated -= _rawStream.ReleaseClusters(focusVcn, count);
|
||||
}
|
||||
else if (result == CompressionResult.Compressed && (_attr.CompressionUnitSize * _bytesPerCluster) - compressedLength > _bytesPerCluster)
|
||||
{
|
||||
int compClusters = Utilities.Ceil(compressedLength, _bytesPerCluster);
|
||||
totalAllocated += _rawStream.AllocateClusters(focusVcn, compClusters);
|
||||
totalAllocated += _rawStream.WriteClusters(focusVcn, compClusters, _ioBuffer, 0);
|
||||
totalAllocated -= _rawStream.ReleaseClusters(focusVcn + compClusters, _attr.CompressionUnitSize - compClusters);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalAllocated += _rawStream.AllocateClusters(focusVcn, _attr.CompressionUnitSize);
|
||||
totalAllocated += _rawStream.WriteClusters(focusVcn, _attr.CompressionUnitSize, buffer, offset);
|
||||
}
|
||||
|
||||
return totalAllocated;
|
||||
}
|
||||
|
||||
private long CompressionStart(long vcn)
|
||||
{
|
||||
return Utilities.RoundDown(vcn, _attr.CompressionUnitSize);
|
||||
}
|
||||
|
||||
private void LoadCache(long vcn)
|
||||
{
|
||||
long cuStart = CompressionStart(vcn);
|
||||
if (_cacheBufferVcn != cuStart)
|
||||
{
|
||||
if (_rawStream.AreAllClustersStored(cuStart, _attr.CompressionUnitSize))
|
||||
{
|
||||
// Uncompressed data - read straight into cache buffer
|
||||
_rawStream.ReadClusters(cuStart, _attr.CompressionUnitSize, _cacheBuffer, 0);
|
||||
}
|
||||
else if (_rawStream.IsClusterStored(cuStart))
|
||||
{
|
||||
// Compressed data - read via IO buffer
|
||||
_rawStream.ReadClusters(cuStart, _attr.CompressionUnitSize, _ioBuffer, 0);
|
||||
|
||||
int expected = (int)Math.Min(_attr.Length - (vcn * _bytesPerCluster), _attr.CompressionUnitSize * _bytesPerCluster);
|
||||
|
||||
int decomp = _context.Options.Compressor.Decompress(_ioBuffer, 0, _ioBuffer.Length, _cacheBuffer, 0);
|
||||
if (decomp < expected)
|
||||
{
|
||||
throw new IOException("Decompression returned too little data");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sparse, wipe cache buffer directly
|
||||
Array.Clear(_cacheBuffer, 0, _cacheBuffer.Length);
|
||||
}
|
||||
|
||||
_cacheBufferVcn = cuStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user