mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-21 06:31:02 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.IO;
|
||||
using DiscUtils;
|
||||
|
||||
/// <summary>
|
||||
/// Low-level non-resident attribute operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Responsible for:
|
||||
/// * Cluster Allocation / Release
|
||||
/// * Reading clusters from disk
|
||||
/// * Writing clusters to disk
|
||||
/// * Substituting zeros for 'sparse'/'unallocated' clusters
|
||||
/// Not responsible for:
|
||||
/// * Compression / Decompression
|
||||
/// * Extending attributes.
|
||||
/// </remarks>
|
||||
internal sealed class RawClusterStream : ClusterStream
|
||||
{
|
||||
private INtfsContext _context;
|
||||
private Stream _fsStream;
|
||||
private int _bytesPerCluster;
|
||||
private CookedDataRuns _cookedRuns;
|
||||
private bool _isMft;
|
||||
|
||||
public RawClusterStream(INtfsContext context, CookedDataRuns cookedRuns, bool isMft)
|
||||
{
|
||||
_context = context;
|
||||
_cookedRuns = cookedRuns;
|
||||
_isMft = isMft;
|
||||
|
||||
_fsStream = _context.RawStream;
|
||||
_bytesPerCluster = context.BiosParameterBlock.BytesPerCluster;
|
||||
}
|
||||
|
||||
public override long AllocatedClusterCount
|
||||
{
|
||||
get
|
||||
{
|
||||
long total = 0;
|
||||
for (int i = 0; i < _cookedRuns.Count; ++i)
|
||||
{
|
||||
CookedDataRun run = _cookedRuns[i];
|
||||
total += run.IsSparse ? 0 : run.Length;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Range<long, long>> StoredClusters
|
||||
{
|
||||
get
|
||||
{
|
||||
Range<long, long> lastVcnRange = null;
|
||||
List<Range<long, long>> ranges = new List<Range<long, long>>();
|
||||
|
||||
int runCount = _cookedRuns.Count;
|
||||
for (int i = 0; i < runCount; i++)
|
||||
{
|
||||
CookedDataRun cookedRun = _cookedRuns[i];
|
||||
if (!cookedRun.IsSparse)
|
||||
{
|
||||
long startPos = cookedRun.StartVcn;
|
||||
if (lastVcnRange != null && lastVcnRange.Offset + lastVcnRange.Count == startPos)
|
||||
{
|
||||
lastVcnRange = new Range<long, long>(lastVcnRange.Offset, lastVcnRange.Count + cookedRun.Length);
|
||||
ranges[ranges.Count - 1] = lastVcnRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastVcnRange = new Range<long, long>(cookedRun.StartVcn, cookedRun.Length);
|
||||
ranges.Add(lastVcnRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsClusterStored(long vcn)
|
||||
{
|
||||
int runIdx = _cookedRuns.FindDataRun(vcn, 0);
|
||||
return !_cookedRuns[runIdx].IsSparse;
|
||||
}
|
||||
|
||||
public bool AreAllClustersStored(long vcn, int count)
|
||||
{
|
||||
int runIdx = 0;
|
||||
long focusVcn = vcn;
|
||||
while (focusVcn < vcn + count)
|
||||
{
|
||||
runIdx = _cookedRuns.FindDataRun(focusVcn, runIdx);
|
||||
|
||||
CookedDataRun run = _cookedRuns[runIdx];
|
||||
if (run.IsSparse)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
focusVcn = run.StartVcn + run.Length;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ExpandToClusters(long numVirtualClusters, NonResidentAttributeRecord extent, bool allocate)
|
||||
{
|
||||
long totalVirtualClusters = _cookedRuns.NextVirtualCluster;
|
||||
if (totalVirtualClusters < numVirtualClusters)
|
||||
{
|
||||
NonResidentAttributeRecord realExtent = extent;
|
||||
if (realExtent == null)
|
||||
{
|
||||
realExtent = _cookedRuns.Last.AttributeExtent;
|
||||
}
|
||||
|
||||
DataRun newRun = new DataRun(0, numVirtualClusters - totalVirtualClusters, true);
|
||||
realExtent.DataRuns.Add(newRun);
|
||||
_cookedRuns.Append(newRun, extent);
|
||||
realExtent.LastVcn = numVirtualClusters - 1;
|
||||
}
|
||||
|
||||
if (allocate)
|
||||
{
|
||||
AllocateClusters(totalVirtualClusters, (int)(numVirtualClusters - totalVirtualClusters));
|
||||
}
|
||||
}
|
||||
|
||||
public override void TruncateToClusters(long numVirtualClusters)
|
||||
{
|
||||
if (numVirtualClusters < _cookedRuns.NextVirtualCluster)
|
||||
{
|
||||
ReleaseClusters(numVirtualClusters, (int)(_cookedRuns.NextVirtualCluster - numVirtualClusters));
|
||||
|
||||
int runIdx = _cookedRuns.FindDataRun(numVirtualClusters, 0);
|
||||
|
||||
if (numVirtualClusters != _cookedRuns[runIdx].StartVcn)
|
||||
{
|
||||
_cookedRuns.SplitRun(runIdx, numVirtualClusters);
|
||||
runIdx++;
|
||||
}
|
||||
|
||||
_cookedRuns.TruncateAt(runIdx);
|
||||
}
|
||||
}
|
||||
|
||||
public int AllocateClusters(long startVcn, int count)
|
||||
{
|
||||
if (startVcn + count > _cookedRuns.NextVirtualCluster)
|
||||
{
|
||||
throw new IOException("Attempt to allocate unknown clusters");
|
||||
}
|
||||
|
||||
int totalAllocated = 0;
|
||||
int runIdx = 0;
|
||||
|
||||
long focus = startVcn;
|
||||
while (focus < startVcn + count)
|
||||
{
|
||||
runIdx = _cookedRuns.FindDataRun(focus, runIdx);
|
||||
CookedDataRun run = _cookedRuns[runIdx];
|
||||
|
||||
if (run.IsSparse)
|
||||
{
|
||||
if (focus != run.StartVcn)
|
||||
{
|
||||
_cookedRuns.SplitRun(runIdx, focus);
|
||||
runIdx++;
|
||||
run = _cookedRuns[runIdx];
|
||||
}
|
||||
|
||||
long numClusters = Math.Min((startVcn + count) - focus, run.Length);
|
||||
if (numClusters != run.Length)
|
||||
{
|
||||
_cookedRuns.SplitRun(runIdx, focus + numClusters);
|
||||
run = _cookedRuns[runIdx];
|
||||
}
|
||||
|
||||
long nextCluster = -1;
|
||||
for (int i = runIdx - 1; i >= 0; --i)
|
||||
{
|
||||
if (!_cookedRuns[i].IsSparse)
|
||||
{
|
||||
nextCluster = _cookedRuns[i].StartLcn + _cookedRuns[i].Length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var alloced = _context.ClusterBitmap.AllocateClusters(numClusters, nextCluster, _isMft, AllocatedClusterCount);
|
||||
|
||||
List<DataRun> runs = new List<DataRun>();
|
||||
|
||||
long lcn = runIdx == 0 ? 0 : _cookedRuns[runIdx - 1].StartLcn;
|
||||
foreach (var allocation in alloced)
|
||||
{
|
||||
runs.Add(new DataRun(allocation.First - lcn, allocation.Second, false));
|
||||
lcn = allocation.First;
|
||||
}
|
||||
|
||||
_cookedRuns.MakeNonSparse(runIdx, runs);
|
||||
|
||||
totalAllocated += (int)numClusters;
|
||||
focus += numClusters;
|
||||
}
|
||||
else
|
||||
{
|
||||
focus = run.StartVcn + run.Length;
|
||||
}
|
||||
}
|
||||
|
||||
return totalAllocated;
|
||||
}
|
||||
|
||||
public int ReleaseClusters(long startVcn, int count)
|
||||
{
|
||||
int runIdx = 0;
|
||||
|
||||
int totalReleased = 0;
|
||||
|
||||
long focus = startVcn;
|
||||
while (focus < startVcn + count)
|
||||
{
|
||||
runIdx = _cookedRuns.FindDataRun(focus, runIdx);
|
||||
CookedDataRun run = _cookedRuns[runIdx];
|
||||
|
||||
if (run.IsSparse)
|
||||
{
|
||||
focus += run.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (focus != run.StartVcn)
|
||||
{
|
||||
_cookedRuns.SplitRun(runIdx, focus);
|
||||
runIdx++;
|
||||
run = _cookedRuns[runIdx];
|
||||
}
|
||||
|
||||
long numClusters = Math.Min((startVcn + count) - focus, run.Length);
|
||||
if (numClusters != run.Length)
|
||||
{
|
||||
_cookedRuns.SplitRun(runIdx, focus + numClusters);
|
||||
run = _cookedRuns[runIdx];
|
||||
}
|
||||
|
||||
_context.ClusterBitmap.FreeClusters(new Range<long, long>(run.StartLcn, run.Length));
|
||||
_cookedRuns.MakeSparse(runIdx);
|
||||
totalReleased += (int)run.Length;
|
||||
|
||||
focus += numClusters;
|
||||
}
|
||||
}
|
||||
|
||||
return totalReleased;
|
||||
}
|
||||
|
||||
public override void ReadClusters(long startVcn, int count, byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.AssertBufferParameters(buffer, offset, count * _bytesPerCluster);
|
||||
|
||||
int runIdx = 0;
|
||||
int totalRead = 0;
|
||||
while (totalRead < count)
|
||||
{
|
||||
long focusVcn = startVcn + totalRead;
|
||||
|
||||
runIdx = _cookedRuns.FindDataRun(focusVcn, runIdx);
|
||||
CookedDataRun run = _cookedRuns[runIdx];
|
||||
|
||||
int toRead = (int)Math.Min(count - totalRead, run.Length - (focusVcn - run.StartVcn));
|
||||
|
||||
if (run.IsSparse)
|
||||
{
|
||||
Array.Clear(buffer, offset + (totalRead * _bytesPerCluster), toRead * _bytesPerCluster);
|
||||
}
|
||||
else
|
||||
{
|
||||
long lcn = _cookedRuns[runIdx].StartLcn + (focusVcn - run.StartVcn);
|
||||
_fsStream.Position = lcn * _bytesPerCluster;
|
||||
int numRead = Utilities.ReadFully(_fsStream, buffer, offset + (totalRead * _bytesPerCluster), toRead * _bytesPerCluster);
|
||||
if (numRead != toRead * _bytesPerCluster)
|
||||
{
|
||||
throw new IOException(string.Format(CultureInfo.InvariantCulture, "Short read, reading {0} clusters starting at LCN {1}", toRead, lcn));
|
||||
}
|
||||
}
|
||||
|
||||
totalRead += toRead;
|
||||
}
|
||||
}
|
||||
|
||||
public override int WriteClusters(long startVcn, int count, byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.AssertBufferParameters(buffer, offset, count * _bytesPerCluster);
|
||||
|
||||
int runIdx = 0;
|
||||
int totalWritten = 0;
|
||||
while (totalWritten < count)
|
||||
{
|
||||
long focusVcn = startVcn + totalWritten;
|
||||
|
||||
runIdx = _cookedRuns.FindDataRun(focusVcn, runIdx);
|
||||
CookedDataRun run = _cookedRuns[runIdx];
|
||||
|
||||
if (run.IsSparse)
|
||||
{
|
||||
throw new NotImplementedException("Writing to sparse datarun");
|
||||
}
|
||||
|
||||
int toWrite = (int)Math.Min(count - totalWritten, run.Length - (focusVcn - run.StartVcn));
|
||||
|
||||
long lcn = _cookedRuns[runIdx].StartLcn + (focusVcn - run.StartVcn);
|
||||
_fsStream.Position = lcn * _bytesPerCluster;
|
||||
_fsStream.Write(buffer, offset + (totalWritten * _bytesPerCluster), toWrite * _bytesPerCluster);
|
||||
|
||||
totalWritten += toWrite;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int ClearClusters(long startVcn, int count)
|
||||
{
|
||||
byte[] zeroBuffer = new byte[16 * _bytesPerCluster];
|
||||
|
||||
int clustersAllocated = 0;
|
||||
|
||||
int numWritten = 0;
|
||||
while (numWritten < count)
|
||||
{
|
||||
int toWrite = Math.Min(count - numWritten, 16);
|
||||
|
||||
clustersAllocated += WriteClusters(startVcn + numWritten, toWrite, zeroBuffer, 0);
|
||||
|
||||
numWritten += toWrite;
|
||||
}
|
||||
|
||||
return -clustersAllocated;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user