mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-18 21:30:15 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,270 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
internal class ClusterBitmap : System.IDisposable
|
||||
{
|
||||
private File _file;
|
||||
private Bitmap _bitmap;
|
||||
|
||||
private long _nextDataCluster;
|
||||
private bool _fragmentedDiskMode;
|
||||
|
||||
public ClusterBitmap(File file)
|
||||
{
|
||||
_file = file;
|
||||
_bitmap = new Bitmap(
|
||||
_file.OpenStream(AttributeType.Data, null, FileAccess.ReadWrite),
|
||||
Utilities.Ceil(file.Context.BiosParameterBlock.TotalSectors64, file.Context.BiosParameterBlock.SectorsPerCluster));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_bitmap != null)
|
||||
{
|
||||
_bitmap.Dispose();
|
||||
_bitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates clusters from the disk.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of clusters to allocate.</param>
|
||||
/// <param name="proposedStart">The proposed start cluster (or -1).</param>
|
||||
/// <param name="isMft"><c>true</c> if this attribute is the $MFT\$DATA attribute.</param>
|
||||
/// <param name="total">The total number of clusters in the file, including this allocation.</param>
|
||||
/// <returns>The list of cluster allocations.</returns>
|
||||
public Tuple<long, long>[] AllocateClusters(long count, long proposedStart, bool isMft, long total)
|
||||
{
|
||||
List<Tuple<long, long>> result = new List<Tuple<long, long>>();
|
||||
|
||||
long numFound = 0;
|
||||
|
||||
long totalClusters = _file.Context.RawStream.Length / _file.Context.BiosParameterBlock.BytesPerCluster;
|
||||
|
||||
if (isMft)
|
||||
{
|
||||
// First, try to extend the existing cluster run (if available)
|
||||
if (proposedStart >= 0)
|
||||
{
|
||||
numFound += ExtendRun(count - numFound, result, proposedStart, totalClusters);
|
||||
}
|
||||
|
||||
// The MFT grows sequentially across the disk
|
||||
if (numFound < count && !_fragmentedDiskMode)
|
||||
{
|
||||
numFound += FindClusters(count - numFound, result, 0, totalClusters, isMft, true, 0);
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
numFound += FindClusters(count - numFound, result, 0, totalClusters, isMft, false, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// First, try to extend the existing cluster run (if available)
|
||||
if (proposedStart >= 0)
|
||||
{
|
||||
numFound += ExtendRun(count - numFound, result, proposedStart, totalClusters);
|
||||
}
|
||||
|
||||
// Try to find a contiguous range
|
||||
if (numFound < count && !_fragmentedDiskMode)
|
||||
{
|
||||
numFound += FindClusters(count - numFound, result, totalClusters / 8, totalClusters, isMft, true, total / 4);
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
numFound += FindClusters(count - numFound, result, totalClusters / 8, totalClusters, isMft, false, 0);
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
numFound = FindClusters(count - numFound, result, totalClusters / 16, totalClusters / 8, isMft, false, 0);
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
numFound = FindClusters(count - numFound, result, totalClusters / 32, totalClusters / 16, isMft, false, 0);
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
numFound = FindClusters(count - numFound, result, 0, totalClusters / 32, isMft, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (numFound < count)
|
||||
{
|
||||
FreeClusters(result.ToArray());
|
||||
throw new IOException("Out of disk space");
|
||||
}
|
||||
|
||||
// If we found more than two clusters, or we have a fragmented result,
|
||||
// then switch out of trying to allocate contiguous ranges. Similarly,
|
||||
// switch back if we found a resonable quantity in a single span.
|
||||
if ((numFound > 4 && result.Count == 1) || result.Count > 1)
|
||||
{
|
||||
_fragmentedDiskMode = (numFound / result.Count) < 4;
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
internal void MarkAllocated(long first, long count)
|
||||
{
|
||||
_bitmap.MarkPresentRange(first, count);
|
||||
}
|
||||
|
||||
internal void FreeClusters(params Tuple<long, long>[] runs)
|
||||
{
|
||||
foreach (var run in runs)
|
||||
{
|
||||
_bitmap.MarkAbsentRange(run.First, run.Second);
|
||||
}
|
||||
}
|
||||
|
||||
internal void FreeClusters(params Range<long, long>[] runs)
|
||||
{
|
||||
foreach (var run in runs)
|
||||
{
|
||||
_bitmap.MarkAbsentRange(run.Offset, run.Count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the total number of clusters managed in the volume.
|
||||
/// </summary>
|
||||
/// <param name="numClusters">Total number of clusters in the volume.</param>
|
||||
/// <remarks>
|
||||
/// Any clusters represented in the bitmap beyond the total number in the volume are marked as in-use.
|
||||
/// </remarks>
|
||||
internal void SetTotalClusters(long numClusters)
|
||||
{
|
||||
long actualClusters = _bitmap.SetTotalEntries(numClusters);
|
||||
if (actualClusters != numClusters)
|
||||
{
|
||||
MarkAllocated(numClusters, actualClusters - numClusters);
|
||||
}
|
||||
}
|
||||
|
||||
private long ExtendRun(long count, List<Tuple<long, long>> result, long start, long end)
|
||||
{
|
||||
long focusCluster = start;
|
||||
while (!_bitmap.IsPresent(focusCluster) && focusCluster < end && focusCluster - start < count)
|
||||
{
|
||||
++focusCluster;
|
||||
}
|
||||
|
||||
long numFound = focusCluster - start;
|
||||
|
||||
if (numFound > 0)
|
||||
{
|
||||
_bitmap.MarkPresentRange(start, numFound);
|
||||
result.Add(new Tuple<long, long>(start, numFound));
|
||||
}
|
||||
|
||||
return numFound;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds one or more free clusters in a range.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of clusters required.</param>
|
||||
/// <param name="result">The list of clusters found (i.e. out param).</param>
|
||||
/// <param name="start">The first cluster in the range to look at.</param>
|
||||
/// <param name="end">The last cluster in the range to look at (exclusive).</param>
|
||||
/// <param name="isMft">Indicates if the clusters are for the MFT.</param>
|
||||
/// <param name="contiguous">Indicates if contiguous clusters are required.</param>
|
||||
/// <param name="headroom">Indicates how many clusters to skip before next allocation, to prevent fragmentation.</param>
|
||||
/// <returns>The number of clusters found in the range.</returns>
|
||||
private long FindClusters(long count, List<Tuple<long, long>> result, long start, long end, bool isMft, bool contiguous, long headroom)
|
||||
{
|
||||
long numFound = 0;
|
||||
|
||||
long focusCluster;
|
||||
if (isMft)
|
||||
{
|
||||
focusCluster = start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_nextDataCluster < start || _nextDataCluster >= end)
|
||||
{
|
||||
_nextDataCluster = start;
|
||||
}
|
||||
|
||||
focusCluster = _nextDataCluster;
|
||||
}
|
||||
|
||||
long numInspected = 0;
|
||||
while (numFound < count && focusCluster >= start && numInspected < end - start)
|
||||
{
|
||||
if (!_bitmap.IsPresent(focusCluster))
|
||||
{
|
||||
// Start of a run...
|
||||
long runStart = focusCluster;
|
||||
++focusCluster;
|
||||
|
||||
while (!_bitmap.IsPresent(focusCluster) && focusCluster - runStart < (count - numFound))
|
||||
{
|
||||
++focusCluster;
|
||||
++numInspected;
|
||||
}
|
||||
|
||||
if (!contiguous || (focusCluster - runStart) == (count - numFound))
|
||||
{
|
||||
_bitmap.MarkPresentRange(runStart, focusCluster - runStart);
|
||||
|
||||
result.Add(new Tuple<long, long>(runStart, focusCluster - runStart));
|
||||
numFound += focusCluster - runStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++focusCluster;
|
||||
}
|
||||
|
||||
++numInspected;
|
||||
|
||||
if (focusCluster >= end)
|
||||
{
|
||||
focusCluster = start;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMft)
|
||||
{
|
||||
_nextDataCluster = focusCluster + headroom;
|
||||
}
|
||||
|
||||
return numFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user