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

322 lines
10 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;
internal class CookedDataRuns
{
private List<CookedDataRun> _runs;
private int _firstDirty = int.MaxValue;
private int _lastDirty = 0;
public CookedDataRuns()
{
_runs = new List<CookedDataRun>();
}
public CookedDataRuns(IEnumerable<DataRun> rawRuns, NonResidentAttributeRecord attributeExtent)
{
_runs = new List<CookedDataRun>();
Append(rawRuns, attributeExtent);
}
public long NextVirtualCluster
{
get
{
if (_runs.Count == 0)
{
return 0;
}
else
{
int lastRun = _runs.Count - 1;
return _runs[lastRun].StartVcn + _runs[lastRun].Length;
}
}
}
public CookedDataRun Last
{
get
{
if (_runs.Count == 0)
{
return null;
}
else
{
return _runs[_runs.Count - 1];
}
}
}
public int Count
{
get { return _runs.Count; }
}
public CookedDataRun this[int index]
{
get { return _runs[index]; }
}
public int FindDataRun(long vcn, int startIdx)
{
int numRuns = _runs.Count;
if (numRuns > 0)
{
CookedDataRun run = _runs[numRuns - 1];
if (vcn >= run.StartVcn)
{
if (run.StartVcn + run.Length > vcn)
{
return numRuns - 1;
}
else
{
throw new IOException("Looking for VCN outside of data runs");
}
}
for (int i = startIdx; i < numRuns; ++i)
{
run = _runs[i];
if (run.StartVcn + run.Length > vcn)
{
return i;
}
}
}
throw new IOException("Looking for VCN outside of data runs");
}
public void Append(DataRun rawRun, NonResidentAttributeRecord attributeExtent)
{
CookedDataRun last = Last;
_runs.Add(new CookedDataRun(rawRun, NextVirtualCluster, last == null ? 0 : last.StartLcn, attributeExtent));
}
public void Append(IEnumerable<DataRun> rawRuns, NonResidentAttributeRecord attributeExtent)
{
long vcn = NextVirtualCluster;
long lcn = 0;
foreach (var run in rawRuns)
{
_runs.Add(new CookedDataRun(run, vcn, lcn, attributeExtent));
vcn += run.RunLength;
lcn += run.RunOffset;
}
}
public void MakeSparse(int index)
{
if (index < _firstDirty)
{
_firstDirty = index;
}
if (index > _lastDirty)
{
_lastDirty = index;
}
long prevLcn = index == 0 ? 0 : _runs[index - 1].StartLcn;
CookedDataRun run = _runs[index];
if (run.IsSparse)
{
throw new ArgumentException("Run is already sparse", "index");
}
_runs[index] = new CookedDataRun(new DataRun(0, run.Length, true), run.StartVcn, prevLcn, run.AttributeExtent);
run.AttributeExtent.ReplaceRun(run.DataRun, _runs[index].DataRun);
for (int i = index + 1; i < _runs.Count; ++i)
{
if (!_runs[i].IsSparse)
{
_runs[i].DataRun.RunOffset += run.StartLcn - prevLcn;
break;
}
}
}
public void MakeNonSparse(int index, IEnumerable<DataRun> rawRuns)
{
if (index < _firstDirty)
{
_firstDirty = index;
}
if (index > _lastDirty)
{
_lastDirty = index;
}
long prevLcn = index == 0 ? 0 : _runs[index - 1].StartLcn;
CookedDataRun run = _runs[index];
if (!run.IsSparse)
{
throw new ArgumentException("Run is already non-sparse", "index");
}
_runs.RemoveAt(index);
int insertIdx = run.AttributeExtent.RemoveRun(run.DataRun);
CookedDataRun lastNewRun = null;
long lcn = prevLcn;
long vcn = run.StartVcn;
foreach (var rawRun in rawRuns)
{
CookedDataRun newRun = new CookedDataRun(rawRun, vcn, lcn, run.AttributeExtent);
_runs.Insert(index, newRun);
run.AttributeExtent.InsertRun(insertIdx, rawRun);
vcn += rawRun.RunLength;
lcn += rawRun.RunOffset;
lastNewRun = newRun;
insertIdx++;
index++;
}
for (int i = index; i < _runs.Count; ++i)
{
if (_runs[i].IsSparse)
{
_runs[i].StartLcn = lastNewRun.StartLcn;
}
else
{
_runs[i].DataRun.RunOffset = _runs[i].StartLcn - lastNewRun.StartLcn;
break;
}
}
}
public void SplitRun(int runIdx, long vcn)
{
if (runIdx < _firstDirty)
{
_firstDirty = runIdx;
}
if (runIdx > _lastDirty)
{
_lastDirty = runIdx;
}
CookedDataRun run = _runs[runIdx];
if (run.StartVcn >= vcn || run.StartVcn + run.Length <= vcn)
{
throw new ArgumentException("Attempt to split run outside of it's range", "vcn");
}
long distance = vcn - run.StartVcn;
long offset = run.IsSparse ? 0 : distance;
CookedDataRun newRun = new CookedDataRun(new DataRun(offset, run.Length - distance, run.IsSparse), vcn, run.StartLcn, run.AttributeExtent);
run.Length = distance;
_runs.Insert(runIdx + 1, newRun);
run.AttributeExtent.InsertRun(run.DataRun, newRun.DataRun);
for (int i = runIdx + 2; i < _runs.Count; ++i)
{
if (_runs[i].IsSparse)
{
_runs[i].StartLcn += offset;
}
else
{
_runs[i].DataRun.RunOffset -= offset;
break;
}
}
}
/// <summary>
/// Truncates the set of data runs.
/// </summary>
/// <param name="index">The first run to be truncated.</param>
public void TruncateAt(int index)
{
while (index < _runs.Count)
{
_runs[index].AttributeExtent.RemoveRun(_runs[index].DataRun);
_runs.RemoveAt(index);
}
}
internal void CollapseRuns()
{
int i = _firstDirty > 1 ? _firstDirty - 1 : 0;
while (i < _runs.Count - 1 && i <= _lastDirty + 1)
{
if (_runs[i].IsSparse && _runs[i + 1].IsSparse)
{
_runs[i].Length += _runs[i + 1].Length;
_runs[i + 1].AttributeExtent.RemoveRun(_runs[i + 1].DataRun);
_runs.RemoveAt(i + 1);
}
else if (!_runs[i].IsSparse && !_runs[i].IsSparse && _runs[i].StartLcn + _runs[i].Length == _runs[i + 1].StartLcn)
{
_runs[i].Length += _runs[i + 1].Length;
_runs[i + 1].AttributeExtent.RemoveRun(_runs[i + 1].DataRun);
_runs.RemoveAt(i + 1);
for (int j = i + 1; j < _runs.Count; ++j)
{
if (_runs[j].IsSparse)
{
_runs[j].StartLcn = _runs[i].StartLcn;
}
else
{
_runs[j].DataRun.RunOffset = _runs[j].StartLcn - _runs[i].StartLcn;
break;
}
}
}
else
{
++i;
}
}
_firstDirty = int.MaxValue;
_lastDirty = 0;
}
}
}