mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-15 03:46:40 +10:00
170 lines
5.9 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|