587 lines
23 KiB
Plaintext
587 lines
23 KiB
Plaintext
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Fat12.sg
|
|
//
|
|
// This code bangs hopelessly on the buffer cache. 12 bit
|
|
// pointers equates to pointers that span byte and sector
|
|
// boundaries. These are painful to work with. To date no
|
|
// effort has been made to optimize the chain allocation, grow,
|
|
// free, and truncate routines.
|
|
//
|
|
|
|
//#define DEBUG_FAT12 // Turn on sanity checks such as they are
|
|
|
|
using Microsoft.SingSharp;
|
|
using Microsoft.Singularity.Io;
|
|
using Microsoft.Singularity.Channels;
|
|
|
|
using System;
|
|
|
|
namespace Microsoft.Singularity.Services.Fat.Fs
|
|
{
|
|
internal sealed class Fat12 : Fat
|
|
{
|
|
Fat12Internal fatInternal;
|
|
|
|
internal Fat12(BlockCache! theBlockCache,
|
|
BpbSummary! theBpbSummary)
|
|
{
|
|
fatInternal = new Fat12Internal(theBlockCache, theBpbSummary);
|
|
}
|
|
|
|
internal override bool AllocateChain(int hintClusterArea,
|
|
int targetLength,
|
|
out int allocStart,
|
|
out int allocLength)
|
|
{
|
|
return fatInternal.AllocateChain(hintClusterArea, targetLength,
|
|
out allocStart, out allocLength);
|
|
}
|
|
|
|
internal override void FreeChain(int startCluster)
|
|
{
|
|
fatInternal.FreeChain(startCluster);
|
|
}
|
|
|
|
internal override bool GrowChain(BlockIndex! index,
|
|
int requestedExtensionLength,
|
|
out int actualExtensionLength)
|
|
{
|
|
return fatInternal.GrowChain(index, requestedExtensionLength,
|
|
out actualExtensionLength);
|
|
}
|
|
|
|
internal override void TruncateChain(BlockIndex! index,
|
|
int lengthInClusters)
|
|
{
|
|
fatInternal.TruncateChain(index, lengthInClusters);
|
|
}
|
|
|
|
internal override void PopulateIndex(BlockIndex! index,
|
|
int firstCluster)
|
|
{
|
|
fatInternal.PopulateIndex(index, firstCluster);
|
|
}
|
|
|
|
internal override bool CleanShutdown
|
|
{
|
|
get { return fatInternal.CleanShutdown; }
|
|
set { fatInternal.CleanShutdown = value; }
|
|
}
|
|
|
|
internal override bool HardError
|
|
{
|
|
get { return fatInternal.HardError; }
|
|
set { fatInternal.HardError = value; }
|
|
}
|
|
|
|
internal override int TotalClusters
|
|
{
|
|
get { return fatInternal.TotalClusters; }
|
|
}
|
|
|
|
internal override int EndOfChain
|
|
{
|
|
get { return fatInternal.EndOfChain; }
|
|
}
|
|
|
|
internal override int BadCluster
|
|
{
|
|
get { return fatInternal.BadCluster; }
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Internal representation of FAT12 structure
|
|
|
|
private sealed class Fat12Internal
|
|
{
|
|
private const int UnallocatedMarker = 0;
|
|
private const int EndOfChainMarker = 0xfff;
|
|
private const int EndOfChainMinimum = 0xff8;
|
|
private const int BadClusterMarker = 0xff7;
|
|
public const int MaxClusters = 0xff7;
|
|
public const int ReservedClusters = 2;
|
|
|
|
private const ushort Visited = 0xffff; // Used by DEBUG_FAT12
|
|
|
|
private BlockCache! blockCache;
|
|
private BpbSummary! bpbSummary;
|
|
private uint preferredFat;
|
|
private Bitmap! bitmap;
|
|
private bool cleanShutdown;
|
|
private bool hardError;
|
|
|
|
[ Microsoft.Contracts.NotDelayed ]
|
|
internal Fat12Internal(BlockCache! theBlockCache,
|
|
BpbSummary! theBpbSummary)
|
|
requires theBpbSummary.Version == FatVersion.Fat12;
|
|
requires theBpbSummary.SectorsPerFat >= 1;
|
|
requires theBpbSummary.NumberOfFats >= 1;
|
|
requires theBpbSummary.ClusterCount >= 1;
|
|
{
|
|
this.blockCache = theBlockCache;
|
|
this.bpbSummary = theBpbSummary;
|
|
this.preferredFat = 0;
|
|
this.bitmap =
|
|
new Bitmap((int)theBpbSummary.ClusterCount + ReservedClusters);
|
|
this.cleanShutdown = true;
|
|
this.hardError = false;
|
|
base();
|
|
InitializeBitmap(this.preferredFat);
|
|
}
|
|
|
|
internal bool CleanShutdown
|
|
{
|
|
get { return this.cleanShutdown; }
|
|
set { this.cleanShutdown = value; }
|
|
}
|
|
|
|
internal bool HardError
|
|
{
|
|
get { return this.hardError; }
|
|
set { this.hardError = value; }
|
|
}
|
|
|
|
internal int TotalClusters
|
|
{
|
|
get {
|
|
return (int)bpbSummary.ClusterCount + ReservedClusters;
|
|
}
|
|
}
|
|
|
|
internal int EndOfChain
|
|
{
|
|
get { return EndOfChainMarker; }
|
|
}
|
|
|
|
internal int BadCluster
|
|
{
|
|
get { return BadClusterMarker; }
|
|
}
|
|
|
|
private void InitializeBitmap(uint fat)
|
|
{
|
|
// Mark the two reserved clusters as used
|
|
int allocStart, allocLength;
|
|
bitmap.Allocate(0, ReservedClusters,
|
|
out allocStart, out allocLength);
|
|
DebugStub.Assert(allocStart == 0);
|
|
DebugStub.Assert(allocLength == ReservedClusters);
|
|
|
|
// Scan clusters to find those free.
|
|
for (int i = ReservedClusters; i < bitmap.Length; i++) {
|
|
int next;
|
|
GetNextInFat(fat, i, out next);
|
|
if (next != UnallocatedMarker) {
|
|
bitmap.Allocate(i, 1, out allocStart, out allocLength);
|
|
DebugStub.Assert(allocStart == i);
|
|
DebugStub.Assert(allocLength == 1);
|
|
}
|
|
}
|
|
DumpFat("Initial FAT");
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Debugging routines
|
|
|
|
[ System.Diagnostics.Conditional("DEBUG_FAT12") ]
|
|
private void DumpFat(string! title)
|
|
{
|
|
DebugStub.Print("{0}\n", __arglist(title));
|
|
for (int i = 0; i < TotalClusters; i++) {
|
|
int next;
|
|
GetNextInFat(0, i, out next);
|
|
if (next != 0) {
|
|
DebugStub.Print("Fat[{0:x8}] = {1:x8}\n",
|
|
__arglist(i, next));
|
|
}
|
|
|
|
for (uint fat = 1; fat < bpbSummary.NumberOfFats; fat++) {
|
|
int onext;
|
|
GetNextInFat(fat, i, out onext);
|
|
DebugStub.Assert(onext == next);
|
|
}
|
|
}
|
|
FollowFat();
|
|
}
|
|
|
|
[ System.Diagnostics.Conditional("DEBUG_FAT12") ]
|
|
private void FollowFat()
|
|
{
|
|
ushort [] clusters = new ushort [TotalClusters];
|
|
for (int i = 0; i < TotalClusters; i++) {
|
|
int next;
|
|
GetNextInFat(0, i, out next);
|
|
clusters[i] = (ushort)next;
|
|
}
|
|
|
|
for (int i = ReservedClusters; i < TotalClusters; i++) {
|
|
if ((int)clusters[i] == Visited ||
|
|
(int)clusters[i] == UnallocatedMarker) {
|
|
continue;
|
|
}
|
|
VisitChain(clusters, i);
|
|
}
|
|
}
|
|
|
|
[ System.Diagnostics.Conditional("DEBUG_FAT12") ]
|
|
private void VisitChain(ushort []! clusters, int start)
|
|
{
|
|
while (start < EndOfChainMinimum) {
|
|
if (start < ReservedClusters || start == Visited) {
|
|
DebugStub.Break();
|
|
}
|
|
int next = clusters[start];
|
|
clusters[start] = Visited;
|
|
start = next;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Chain growth and creation related methods
|
|
|
|
internal bool GrowChain(BlockIndex! index,
|
|
int clustersToAdd,
|
|
out int clustersAdded)
|
|
requires index.Count > 0;
|
|
requires clustersToAdd > 0;
|
|
{
|
|
if (clustersToAdd > Bitmap.MaxAllocationLength) {
|
|
clustersToAdd = Bitmap.MaxAllocationLength;
|
|
}
|
|
|
|
lock (this) {
|
|
int tail;
|
|
if (index.Lookup(index.Count - 1, out tail) == false) {
|
|
assert false;
|
|
}
|
|
|
|
int newTailStart;
|
|
if (LockedAllocateChain(tail, clustersToAdd, index,
|
|
out newTailStart,
|
|
out clustersAdded)) {
|
|
SetNext(tail, newTailStart);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal bool AllocateChain(int hintStart,
|
|
int targetLength,
|
|
out int allocStart,
|
|
out int allocLength)
|
|
requires hintStart < Fat12Internal.MaxClusters;
|
|
requires targetLength > 0;
|
|
{
|
|
if (targetLength > Bitmap.MaxAllocationLength) {
|
|
targetLength = Bitmap.MaxAllocationLength;
|
|
}
|
|
lock (this) {
|
|
return LockedAllocateChain(hintStart, targetLength, null,
|
|
out allocStart,
|
|
out allocLength);
|
|
}
|
|
}
|
|
|
|
private void LockedBlockIndexAppend(BlockIndex index,
|
|
int tail,
|
|
int length)
|
|
{
|
|
if (index != null) {
|
|
index.Append(tail, length);
|
|
}
|
|
}
|
|
|
|
private bool LockedAllocateChain(int tail,
|
|
int clustersToAdd,
|
|
BlockIndex index,
|
|
out int newTailStart,
|
|
out int newTailAllocated)
|
|
{
|
|
DebugStub.Assert(clustersToAdd >= 1);
|
|
if (!bitmap.Allocate(tail, 1,
|
|
out newTailStart,
|
|
out newTailAllocated)) {
|
|
return false;
|
|
}
|
|
LockedBlockIndexAppend(index, newTailStart, newTailAllocated);
|
|
|
|
int current = newTailStart;
|
|
while (newTailAllocated != clustersToAdd) {
|
|
int delta, next;
|
|
if (!bitmap.Allocate(current, 1, out next, out delta)) {
|
|
break;
|
|
}
|
|
DebugStub.Assert(delta == 1);
|
|
LockedBlockIndexAppend(index, next, delta);
|
|
newTailAllocated++;
|
|
SetNext(current, next);
|
|
current = next;
|
|
}
|
|
SetNext(current, EndOfChainMarker);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Chain truncation and deletion related methods
|
|
|
|
internal void TruncateChain(BlockIndex! index,
|
|
int clusterLength)
|
|
requires clusterLength >= 0 && clusterLength < index.Count;
|
|
{
|
|
if (clusterLength == index.Count) {
|
|
return;
|
|
}
|
|
|
|
if (clusterLength == 0) {
|
|
int headCluster;
|
|
if (index.Lookup(0, out headCluster) == false) {
|
|
assert false;
|
|
}
|
|
FreeChain(headCluster);
|
|
index.TruncateToLength(clusterLength);
|
|
return;
|
|
}
|
|
|
|
int newTail;
|
|
if (index.Lookup(clusterLength - 1, out newTail) == false) {
|
|
assert false;
|
|
}
|
|
|
|
int zap;
|
|
if (index.Lookup(clusterLength, out zap) == false) {
|
|
assert false;
|
|
}
|
|
|
|
lock (this) {
|
|
SetNext(newTail, EndOfChainMarker);
|
|
LockedFreeChain(zap);
|
|
}
|
|
index.TruncateToLength(clusterLength);
|
|
}
|
|
|
|
internal void FreeChain(int startCluster)
|
|
requires (startCluster >= ReservedClusters &&
|
|
startCluster < MaxClusters);
|
|
{
|
|
lock (this) {
|
|
LockedFreeChain(startCluster);
|
|
}
|
|
}
|
|
|
|
private void LockedFreeChain(int startCluster)
|
|
{
|
|
int nextCluster;
|
|
do {
|
|
if (GetNext(startCluster, out nextCluster) == false) {
|
|
break;
|
|
}
|
|
bitmap.Free(startCluster, 1);
|
|
SetNext(startCluster, UnallocatedMarker);
|
|
startCluster = nextCluster;
|
|
DebugStub.Assert(startCluster >= ReservedClusters);
|
|
} while (startCluster < MaxClusters);
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Populate
|
|
|
|
internal void PopulateIndex(BlockIndex! index,
|
|
int firstCluster)
|
|
requires firstCluster >= Fat12Internal.ReservedClusters;
|
|
requires index.Count == 0;
|
|
{
|
|
int totalClusters = this.TotalClusters;
|
|
int cluster = firstCluster;
|
|
do {
|
|
index.Append(cluster);
|
|
} while (GetNext(cluster, out cluster) != false &&
|
|
cluster >= ReservedClusters &&
|
|
cluster < totalClusters);
|
|
|
|
#if DEBUG_FAT12
|
|
int lastCluster;
|
|
index.Lookup(index.Count - 1, out lastCluster);
|
|
int nextCluster;
|
|
GetNextInFat(0, lastCluster, out nextCluster);
|
|
DebugStub.Assert(nextCluster == EndOfChainMarker);
|
|
#endif // DEBUG_FAT12
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Single link traversal and modification methods
|
|
|
|
private void GetNextInFat(uint fat, int cluster, out int next)
|
|
{
|
|
int fatOffset = cluster + cluster / 2;
|
|
int sectorOffset = fatOffset / (int)bpbSummary.BytesPerSector;
|
|
int entryOffset = fatOffset % (int)bpbSummary.BytesPerSector;
|
|
int sector = (int)(bpbSummary.FirstFatSector +
|
|
fat * bpbSummary.SectorsPerFat +
|
|
sectorOffset);
|
|
|
|
byte b0, b1;
|
|
if (entryOffset == bpbSummary.BytesPerSector - 1) {
|
|
byte[]! in ExHeap blockData0 =
|
|
blockCache.BeginQuickBlockOperation((uint)sector);
|
|
b0 = blockData0[entryOffset];
|
|
blockCache.EndQuickBlockOperation((uint)sector,
|
|
blockData0, false);
|
|
|
|
byte[]! in ExHeap blockData1 =
|
|
blockCache.BeginQuickBlockOperation((uint)sector + 1);
|
|
b1 = blockData1[0];
|
|
blockCache.EndQuickBlockOperation((uint)sector + 1,
|
|
blockData1, false);
|
|
}
|
|
else {
|
|
byte[]! in ExHeap blockData =
|
|
blockCache.BeginQuickBlockOperation((uint)sector);
|
|
b0 = blockData[entryOffset];
|
|
b1 = blockData[entryOffset + 1];
|
|
blockCache.EndQuickBlockOperation((uint)sector,
|
|
blockData, false);
|
|
}
|
|
|
|
GetClusterValue(cluster, b0, b1, out next);
|
|
|
|
// Fix the out value so even
|
|
// non-MS formatted filesystems appear to show the
|
|
// same EOC marker. NB First two clusters are not used
|
|
// and their next pointers may have special purposes.
|
|
if (cluster >= ReservedClusters &&
|
|
next >= EndOfChainMinimum) {
|
|
next = EndOfChainMarker;
|
|
}
|
|
}
|
|
|
|
private static void GetClusterValue(int cluster,
|
|
byte b0,
|
|
byte b1,
|
|
out int next)
|
|
{
|
|
int tmp = (((int)b1) << 8) + (int)b0;
|
|
if ((cluster & 0x1) == 0x1) {
|
|
tmp >>= 4;
|
|
}
|
|
else {
|
|
tmp &= 0xfff;
|
|
}
|
|
next = tmp;
|
|
}
|
|
|
|
private static void SetClusterValue(int cluster,
|
|
int next,
|
|
ref byte b0,
|
|
ref byte b1)
|
|
{
|
|
DebugStub.Assert(cluster >= ReservedClusters &&
|
|
cluster <= EndOfChainMarker &&
|
|
next <= EndOfChainMarker);
|
|
|
|
if ((cluster & 0x1) == 0x1) {
|
|
b0 = (byte)((b0 & 0xf) + ((next << 4) & 0xff));
|
|
b1 = (byte)((next >> 4) & 0xff);
|
|
}
|
|
else {
|
|
b0 = (byte)(next & 0xff);
|
|
b1 = (byte)((b1 & 0xf0) + (next >> 8));
|
|
}
|
|
}
|
|
|
|
private void SetNextInFat(uint fat, int cluster, int next)
|
|
{
|
|
int fatOffset = cluster + cluster / 2;
|
|
int sectorOffset = fatOffset / (int)bpbSummary.BytesPerSector;
|
|
int entryOffset = fatOffset % (int)bpbSummary.BytesPerSector;
|
|
int sector = (int)(bpbSummary.FirstFatSector +
|
|
fat * bpbSummary.SectorsPerFat +
|
|
sectorOffset);
|
|
|
|
if (entryOffset == bpbSummary.BytesPerSector - 1) {
|
|
byte dummy = 0;
|
|
|
|
byte[]! in ExHeap blockData0 =
|
|
blockCache.BeginQuickBlockOperation((uint)sector);
|
|
|
|
SetClusterValue(cluster, next,
|
|
ref blockData0[entryOffset],
|
|
ref dummy);
|
|
|
|
blockCache.EndQuickBlockOperation((uint)sector + 0,
|
|
blockData0, true);
|
|
|
|
byte[]! in ExHeap blockData1 =
|
|
blockCache.BeginQuickBlockOperation((uint)sector + 1);
|
|
|
|
SetClusterValue(cluster, next,
|
|
ref dummy,
|
|
ref blockData1[0]);
|
|
|
|
blockCache.EndQuickBlockOperation((uint)sector + 1,
|
|
blockData1, true);
|
|
}
|
|
else {
|
|
byte[]! in ExHeap blockData =
|
|
blockCache.BeginQuickBlockOperation((uint)sector);
|
|
|
|
SetClusterValue(cluster, next,
|
|
ref blockData[entryOffset],
|
|
ref blockData[entryOffset + 1]);
|
|
|
|
blockCache.EndQuickBlockOperation((uint)sector,
|
|
blockData, true);
|
|
}
|
|
|
|
#if DEBUG_FAT12
|
|
int got;
|
|
GetNextInFat(fat, cluster, out got);
|
|
DebugStub.Assert(got == next);
|
|
#endif // DEBUG_FAT12
|
|
}
|
|
|
|
private bool GetNext(int cluster, out int next)
|
|
{
|
|
DebugStub.Assert(cluster < TotalClusters);
|
|
|
|
for (uint i = 0; i < bpbSummary.NumberOfFats; i++) {
|
|
try {
|
|
GetNextInFat(preferredFat, cluster, out next);
|
|
return true;
|
|
}
|
|
catch (Exception e) {
|
|
DebugStub.WriteLine("Caught {0}", __arglist(e.ToString()));
|
|
}
|
|
preferredFat = (preferredFat + 1) %
|
|
bpbSummary.NumberOfFats;
|
|
}
|
|
|
|
DebugStub.Break();
|
|
next = 0;
|
|
return false;
|
|
// XXX: Fatal error could not read Fat entry
|
|
}
|
|
|
|
private void SetNext(int cluster, int next)
|
|
{
|
|
DebugStub.Assert(cluster < TotalClusters);
|
|
DebugStub.Assert(
|
|
((next < TotalClusters) ||
|
|
(next >= BadClusterMarker && next <= EndOfChainMarker) ||
|
|
(cluster < ReservedClusters))
|
|
);
|
|
|
|
for (uint i = 0; i < bpbSummary.NumberOfFats; i++) {
|
|
SetNextInFat(i, cluster, next);
|
|
}
|
|
}
|
|
} // class Fat12Internal
|
|
} // class Fat12
|
|
}
|