Files
WPinternals/DiscUtils/Ntfs/NonResidentDataBuffer.cs
T
2018-10-25 22:35:49 +02:00

170 lines
5.9 KiB
C#

//
// 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 class NonResidentDataBuffer : DiscUtils.Buffer, IMappedBuffer
{
protected INtfsContext _context;
protected CookedDataRuns _cookedRuns;
protected long _bytesPerCluster;
protected RawClusterStream _rawStream;
protected ClusterStream _activeStream;
protected byte[] _ioBuffer;
public NonResidentDataBuffer(INtfsContext context, NonResidentAttributeRecord record)
: this(context, new CookedDataRuns(record.DataRuns, record), false)
{
}
public NonResidentDataBuffer(INtfsContext context, CookedDataRuns cookedRuns, bool isMft)
{
_context = context;
_cookedRuns = cookedRuns;
_rawStream = new RawClusterStream(_context, _cookedRuns, isMft);
_activeStream = _rawStream;
_bytesPerCluster = _context.BiosParameterBlock.BytesPerCluster;
_ioBuffer = new byte[_bytesPerCluster];
}
public override bool CanRead
{
get { return _context.RawStream.CanRead; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Capacity
{
get { return VirtualClusterCount * _bytesPerCluster; }
}
public long VirtualClusterCount
{
get { return _cookedRuns.NextVirtualCluster; }
}
public override IEnumerable<StreamExtent> Extents
{
get
{
List<StreamExtent> extents = new List<StreamExtent>();
foreach (var range in _activeStream.StoredClusters)
{
extents.Add(new StreamExtent(range.Offset * _bytesPerCluster, range.Count * _bytesPerCluster));
}
return StreamExtent.Intersect(extents, new StreamExtent(0, Capacity));
}
}
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
{
return StreamExtent.Intersect(Extents, new StreamExtent(start, count));
}
public long MapPosition(long pos)
{
long vcn = pos / _bytesPerCluster;
int dataRunIdx = _cookedRuns.FindDataRun(vcn, 0);
if (_cookedRuns[dataRunIdx].IsSparse)
{
return -1;
}
else
{
return (_cookedRuns[dataRunIdx].StartLcn * _bytesPerCluster) + (pos - (_cookedRuns[dataRunIdx].StartVcn * _bytesPerCluster));
}
}
public override int Read(long pos, byte[] buffer, int offset, int count)
{
if (!CanRead)
{
throw new IOException("Attempt to read from file not opened for read");
}
Utilities.AssertBufferParameters(buffer, offset, count);
// Limit read to length of attribute
int totalToRead = (int)Math.Min(count, Capacity - pos);
if (totalToRead <= 0)
{
return 0;
}
long focusPos = pos;
while (focusPos < pos + totalToRead)
{
long vcn = focusPos / _bytesPerCluster;
long remaining = (pos + totalToRead) - focusPos;
long clusterOffset = focusPos - (vcn * _bytesPerCluster);
if (vcn * _bytesPerCluster != focusPos || remaining < _bytesPerCluster)
{
// Unaligned or short read
_activeStream.ReadClusters(vcn, 1, _ioBuffer, 0);
int toRead = (int)Math.Min(remaining, _bytesPerCluster - clusterOffset);
Array.Copy(_ioBuffer, clusterOffset, buffer, offset + (focusPos - pos), toRead);
focusPos += toRead;
}
else
{
// Aligned, full cluster reads...
int fullClusters = (int)(remaining / _bytesPerCluster);
_activeStream.ReadClusters(vcn, fullClusters, buffer, (int)(offset + (focusPos - pos)));
focusPos += fullClusters * _bytesPerCluster;
}
}
return totalToRead;
}
public override void Write(long pos, byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void SetCapacity(long value)
{
throw new NotSupportedException();
}
}
}