singrdk/base/Services/Fat/Fs/Fat12.sg

587 lines
23 KiB
Plaintext
Raw Permalink Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// 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;
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
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) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Caught {0}", __arglist(e.ToString()));
2008-03-05 09:52:00 -05:00
}
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
}