/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: FatVolume.sg // using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Io; using Microsoft.Singularity.Services.Fat.Contracts; using Microsoft.Singularity.V1.Services; using System; namespace Microsoft.Singularity.Services.Fat.Fs { internal sealed class FatVolume { /////////////////////////////////////////////////////////////////////// // Constants const int DiskChannels = 4; /////////////////////////////////////////////////////////////////////// // Static Members static private string diskName; static private Fat! theFat; static private BlockCache! theSectorCache; // Cache of initial-sectors static private BlockCache! theClusterCache; // Cache of data clusters static private BlockWriter! theBlockWriter; // Dirty block flusher static private bool isReadOnly; static private BpbSummary theBpbSummary; static private FileCache! theFileCache; static private DirectoryCache! theDirectoryCache; /////////////////////////////////////////////////////////////////////// // Static Methods internal static bool IsReadOnly { get { return isReadOnly; } } internal static Fat! Fat { get { return theFat; } } internal static BlockCache! NonClusterCache { get { return theSectorCache; } } internal static BlockCache! ClusterCache { get { return theClusterCache; } } internal static BpbSummary! BpbSummary { get { return theBpbSummary; } } internal static DirectoryCache! DirectoryCache { get { return theDirectoryCache; } } internal static FileCache! FileCache { get { return theFileCache; } } internal static FatVersion FatVersion { get { return theBpbSummary.Version; } } private static BpbSummary CreateBpbSummary(string! diskName, [Claims]byte[]! in ExHeap sector0) { try { return new BpbSummary(sector0); } catch (BpbException be) { LogError("Found invalid BPB data on \"{0}\" - {1}.", diskName, be.Message); return null; } finally { delete sector0; } } internal static FatContractErrorCode Mount( string! diskName, bool readOnly, uint cacheMB, uint directoryCacheSize, uint fileCacheSize, uint writeQueueSize) { DirectoryServiceContract.Imp dsImp = DirectoryService.NewClientEndpoint(); try { Disk disk = Disk.Create(dsImp, diskName, DiskChannels); if (disk == null) { LogError("Could not find disk \"{0}\".", diskName); return FatContractErrorCode.DiskUnavailable; } byte [] in ExHeap sector0 = disk.Read(0, 512); if (sector0 == null) { LogError("Failed to read sector 0 of \"{0}\".", diskName); return FatContractErrorCode.ReadFailed; } FatVolume.theBpbSummary = CreateBpbSummary(diskName, sector0); if (theBpbSummary == null) { return FatContractErrorCode.BadBPB; } if (theBpbSummary.TotalSectors > disk.TotalSectors) { LogError("FAT claims more sectors than present on disk" + " ({0} > {1}).", theBpbSummary.TotalSectors, disk.TotalSectors); return FatContractErrorCode.BadBPB; } FatVolume.diskName = diskName; if (readOnly == false) { FatVolume.isReadOnly = ((disk.DiskAttributes & DiskAttributes.ReadOnly) == DiskAttributes.ReadOnly); } else { FatVolume.isReadOnly = readOnly; } // // Initialize write queue // writeQueueSize = Math.Min(writeQueueSize, FatMountSettings.MaxWriteQueueSize); FatVolume.theBlockWriter = new BlockWriter(disk, writeQueueSize, !readOnly); cacheMB = Math.Min(cacheMB, FatMountSettings.MaxCacheMB); // // Initialize cache for portion of disk addressed by sector // uint sectorCacheKB = cacheMB * 1024 / 16; FatVolume.theSectorCache = new BlockCache( new BlockCacheConfiguration( 0, theBpbSummary.BytesPerSector, 1, (uint)theBpbSummary.FirstClusterSector, sectorCacheKB), disk, theBlockWriter); // // Initialize cache for portion of disk addressed by cluster // // The first two entries in // FAT are used for media type and status bits. // // See page 18 of the Fat32 File System Spec (1.03). // uint clusterCacheKB = cacheMB * 1024 - sectorCacheKB; uint clusterZeroOffset = 2 * theBpbSummary.SectorsPerCluster; FatVolume.theClusterCache = new BlockCache( new BlockCacheConfiguration( theBpbSummary.FirstClusterSector - clusterZeroOffset, theBpbSummary.BytesPerSector, theBpbSummary.SectorsPerCluster, theBpbSummary.ClusterCount, clusterCacheKB), disk, theBlockWriter); switch (theBpbSummary.Version) { case FatVersion.Fat12: DebugStub.Print("Mounting Fat12 volume\n"); FatVolume.theFat = new Fat12(theSectorCache, theBpbSummary); break; case FatVersion.Fat16: DebugStub.Print("Mounting Fat16 volume\n"); FatVolume.theFat = new Fat16(theSectorCache, theBpbSummary); break; case FatVersion.Fat32: DebugStub.Print("Mounting Fat32 volume\n"); FatVolume.theFat = new Fat32(theSectorCache, theBpbSummary); break; default: // NOTREACHED assert false; break; } // // Initialize caches for recently closed files and // directories used to mitigate the cost of re-opens // directoryCacheSize = Math.Min(directoryCacheSize, FatMountSettings.MaxDirectoryCacheSize); theDirectoryCache = new DirectoryCache(directoryCacheSize); DebugStub.Print("Directory cache size = {0} directories\n", __arglist(directoryCacheSize)); fileCacheSize = Math.Min(fileCacheSize, FatMountSettings.MaxFileCacheSize); theFileCache = new FileCache(fileCacheSize); DebugStub.Print("File cache size = {0} files\n", __arglist(fileCacheSize)); } finally { delete dsImp; } return FatContractErrorCode.NoError; } internal static void Unmount() { Tracing.Log(Tracing.Debug, "Unmount of {0} beginning.", FatVolume.diskName); FatVolume.theBlockWriter.Shutdown(); NonClusterCache.ValidateAllClean(); ClusterCache.ValidateAllClean(); Tracing.Log(Tracing.Debug, "Unmount of {0} complete.", FatVolume.diskName); } internal static void LogError(string! message) { Tracing.Log(Tracing.Error, message); DebugStub.Print(message); } internal static void LogError(string! message, params object[] values) { LogError(String.Format(message, values)); } } }