mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
369 lines
13 KiB
C#
369 lines
13 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.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;
|
|
}
|
|
}
|
|
}
|