123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using Utilities;
- namespace DiskAccessLibrary.FileSystems.NTFS
- {
- public class ClusterUsageBitmap : NTFSFile
- {
- NTFSVolume m_volume;
- private long m_bufferedClusterVCN = -1;
- private byte[] m_bufferedCluster;
- private bool m_isBufferDirty; // if set to true, we need to write the buffer
- public ClusterUsageBitmap(NTFSVolume volume) : base(volume, MasterFileTable.BitmapSegmentNumber)
- {
- m_volume = volume;
- }
- private byte[] GetBitmapCluster(long bitmapClusterVCN)
- {
- if (m_bufferedClusterVCN != bitmapClusterVCN)
- {
- if (m_isBufferDirty)
- {
- FlushBuffer();
- }
- m_bufferedClusterVCN = bitmapClusterVCN;
- // ClusterUsageBitmap is always non-resident
- m_bufferedCluster = FileRecord.NonResidentDataRecord.ReadDataCluster(Volume, bitmapClusterVCN);
- }
- return m_bufferedCluster;
- }
-
- private void FlushBuffer()
- {
- FileRecord.NonResidentDataRecord.WriteDataClusters(Volume, m_bufferedClusterVCN, m_bufferedCluster);
- m_isBufferDirty = false;
- }
- /// <param name="lcn">Logical cluster number</param>
- private bool IsClusterFree(ulong lcn)
- {
- long bitmapClusterVCN = ((long)(lcn / 8)) / Volume.BytesPerCluster;
- byte[] bitmap = GetBitmapCluster(bitmapClusterVCN);
- int lcnByteOffset = (int)(((long)(lcn / 8)) % Volume.BytesPerCluster);
- int lcnBitIndex = (int)(lcn % 8);
- bool isInUse = ((bitmap[lcnByteOffset] >> lcnBitIndex) & 0x01) != 0;
- return !isInUse;
- }
- private void UpdateClusterStatus(ulong lcn, bool isUsed)
- {
- long bitmapClusterVCN = (long)(lcn / (8 * (uint)Volume.BytesPerCluster));
- // bitmap will reference m_bufferedCluster
- byte[] bitmap = GetBitmapCluster(bitmapClusterVCN);
-
- ulong clusterIndexInBitmap = lcn % (8 * (uint)Volume.BytesPerCluster);
- UpdateClusterStatus(bitmap, clusterIndexInBitmap, true);
-
- m_isBufferDirty = true;
- }
- public KeyValuePairList<ulong, long> AllocateClusters(ulong desiredStartLCN, long numberOfClusters)
- {
- KeyValuePairList<ulong, long> freeClusterRunList = FindClustersToAllocate(desiredStartLCN, numberOfClusters);
- // mark the clusters as used in the volume bitmap
- for (int index = 0; index < freeClusterRunList.Count; index++)
- {
- ulong runStartLCN = freeClusterRunList[index].Key;
- long runLength = freeClusterRunList[index].Value;
- for (uint position = 0; position < runLength; position++)
- {
- UpdateClusterStatus(runStartLCN + position, true);
- }
- }
- if (m_isBufferDirty)
- {
- FlushBuffer();
- }
- return freeClusterRunList;
- }
- /// <summary>
- /// Return list of free cluster runs, key is cluster LCN, value is run length
- /// </summary>
- private KeyValuePairList<ulong, long> FindClustersToAllocate(ulong desiredStartLCN, long numberOfClusters)
- {
- KeyValuePairList<ulong, long> result = new KeyValuePairList<ulong, long>();
- long leftToAllocate;
- ulong endLCN = (ulong)(m_volume.Size / m_volume.BytesPerCluster) - 1;
- KeyValuePairList<ulong, long> segment = FindClustersToAllocate(desiredStartLCN, endLCN, numberOfClusters, out leftToAllocate);
- result.AddRange(segment);
- if (leftToAllocate > 0 && desiredStartLCN > 0)
- {
- segment = FindClustersToAllocate(0, desiredStartLCN - 1, leftToAllocate, out leftToAllocate);
- result.AddRange(segment);
- }
- if (leftToAllocate > 0)
- {
- throw new Exception("Could not allocate clusters. Not enough free disk space");
- }
- return result;
- }
- /// <param name="clustersToAllocate">Number of clusters to allocate</param>
- private KeyValuePairList<ulong, long> FindClustersToAllocate(ulong startLCN, ulong endLCN, long clustersToAllocate, out long leftToAllocate)
- {
- KeyValuePairList<ulong, long> result = new KeyValuePairList<ulong, long>();
- leftToAllocate = clustersToAllocate;
- ulong runStartLCN = 0; // temporary
- long runLength = 0;
- ulong nextLCN = startLCN;
- while (nextLCN <= endLCN && leftToAllocate > 0)
- {
- if (IsClusterFree(nextLCN))
- {
- if (runLength == 0)
- {
- runStartLCN = nextLCN;
- runLength = 1;
- }
- else
- {
- runLength++;
- }
- leftToAllocate--;
- }
- else
- {
- if (runLength > 0)
- {
- result.Add(runStartLCN, runLength);
- runLength = 0;
- // add this run
- }
- }
- nextLCN++;
- }
- // add the last run
- if (runLength > 0)
- {
- result.Add(runStartLCN, runLength);
- }
- return result;
- }
- private byte CountNumberOfFreeClusters(byte bitmap)
- {
- byte result = 0;
- for (int bitIndex = 0; bitIndex < 8; bitIndex++)
- {
- bool isFree = ((bitmap >> bitIndex) & 0x01) == 0;
- if (isFree)
- {
- result++;
- }
- }
- return result;
- }
- // Each bit in the $Bitmap file represents a cluster.
- // The size of the $Bitmap file is always a multiple of 8 bytes, extra bits are always set to 1.
- public long CountNumberOfFreeClusters()
- {
- int transferSizeInClusters = Settings.MaximumTransferSizeLBA / m_volume.SectorsPerCluster;
- ulong endLCN = (ulong)(m_volume.Size / m_volume.BytesPerCluster) - 1;
- long lastClusterVCN = ((long)(endLCN / 8)) / Volume.BytesPerCluster;
-
- long result = 0;
- // build lookup table
- byte[] lookup = new byte[256];
- for (int index = 0; index < 256; index++)
- {
- lookup[index] = CountNumberOfFreeClusters((byte)index);
- }
- // extra bits will be marked as used, so no need for special treatment
- for (long bitmapVcn = 0; bitmapVcn < lastClusterVCN; bitmapVcn += transferSizeInClusters)
- {
- byte[] bitmap = FileRecord.NonResidentDataRecord.ReadDataClusters(Volume, bitmapVcn, transferSizeInClusters);
- for (int index = 0; index < bitmap.Length; index++)
- {
- result += lookup[bitmap[index]];
- }
- }
- return result;
- }
- public static void UpdateClusterStatus(byte[] bitmap, ulong clusterIndexInBitmap, bool isUsed)
- {
- ulong byteOffset = (ulong)clusterIndexInBitmap / 8;
- int bitOffset = (int)clusterIndexInBitmap % 8;
- if (isUsed)
- {
- bitmap[byteOffset] |= (byte)(0x01 << bitOffset);
- }
- else
- {
- bitmap[byteOffset] &= (byte)(~(0x01 << bitOffset));
- }
- }
- }
- }
|