First part of the work on DiskManager

- plugged DriveInfo
- added tests for DriveInfo
- added to VFS a new method RegisterFilesystem()
This commit is contained in:
fanoI 2018-04-13 17:43:00 +02:00
parent fee00817a0
commit d2ecdfdeee
15 changed files with 761 additions and 99 deletions

View file

@ -0,0 +1,136 @@
using System.IO;
using Cosmos.TestRunner;
using Cosmos.Debug.Kernel;
using System;
namespace Cosmos.Kernel.Tests.Fat.System.IO
{
public class DriveInfoTest
{
/// <summary>
/// Tests System.IO.DriveInfo plugs.
/// </summary>
public static void Execute(Debugger mDebugger)
{
string driveName = @"0:\";
var MyDrive = new DriveInfo(driveName);
mDebugger.Send("START TEST: Get Name");
Assert.IsTrue(MyDrive.Name == driveName, "DriveInfo.Name failed drive has wrong name");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get TotalSize");
long MyDriveSize = MyDrive.TotalSize;
mDebugger.Send($"Size is {MyDriveSize}");
Assert.IsTrue(MyDriveSize > 0, "DriveInfo.TotalSize failed: invalid size");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get AvailableFreeSpace");
long MyDriveAvailableFreeSpace = MyDrive.AvailableFreeSpace;
mDebugger.Send($"AvailableFreeSpace {MyDriveAvailableFreeSpace}");
Assert.IsTrue(MyDriveAvailableFreeSpace >= 0, "DriveInfo.AvailableFreeSpace failed: invalid size");
Assert.IsFalse(MyDriveAvailableFreeSpace > MyDrive.TotalSize, "DriveInfo.AvailableFreeSpace failed: more than TotalSize");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get TotalFreeSpace");
long MyDriveTotalFreeSpace = MyDrive.TotalFreeSpace;
mDebugger.Send($"TotalFreeSpace {MyDriveTotalFreeSpace}");
Assert.IsTrue(MyDriveTotalFreeSpace >= 0, "DriveInfo.TotalFreeSpace failed: invalid size");
Assert.IsFalse(MyDriveTotalFreeSpace > MyDrive.TotalSize, "DriveInfo.TotalFreeSpace failed: more than TotalSize");
/*
* If disk quotas are enabled AvailableFreeSpace and TotalFreeSpace could be different numbers but TotalFreeSpace
* should be always >= of AvailableFreeSpace
*/
Assert.IsTrue(MyDriveTotalFreeSpace >= MyDriveAvailableFreeSpace, "DriveInfo.MyDriveTotalFreeSpace failed: less than AvailableFreeSpace");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get RootDirectory");
var xDi = MyDrive.RootDirectory;
Assert.IsTrue(xDi.Name == MyDrive.Name, "RootDirectory failed");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get DriveFormat");
Assert.IsTrue(MyDrive.DriveFormat == "FAT32", "DriveFormat failed");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Get VolumeLabel");
string OriginalVolumeLabel = MyDrive.VolumeLabel;
mDebugger.Send($"Volume Label of {MyDrive.Name} is {MyDrive.VolumeLabel}");
Assert.IsTrue(OriginalVolumeLabel == "COSMOS", "VolumeLabel Get failed not the expected value");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Set VolumeLabel to <TEST>");
// Now try to change it...
String NewVolumeLabel = "TEST";
mDebugger.Send($"Changing Volume Label to {NewVolumeLabel}...");
MyDrive.VolumeLabel = NewVolumeLabel;
string SetVolumeLabel = MyDrive.VolumeLabel;
mDebugger.Send($"Volume Label of {MyDrive.Name} is {SetVolumeLabel}");
Assert.IsTrue(SetVolumeLabel == NewVolumeLabel, "VolumeLabel Set failed: not the expected value");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Set VolumeLabel (restoring original label)");
// Now try to change it...
mDebugger.Send($"Changing Volume Label to {OriginalVolumeLabel}...");
MyDrive.VolumeLabel = OriginalVolumeLabel;
SetVolumeLabel = MyDrive.VolumeLabel;
mDebugger.Send($"Volume Label of {MyDrive.Name} is {SetVolumeLabel}");
Assert.IsTrue(SetVolumeLabel == OriginalVolumeLabel, "VolumeLabel Set failed: not the expected value");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Testing isReady status of the Drive");
Assert.IsTrue(MyDrive.IsReady, "IsReady failed drive not ready");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Testing DriveType");
Assert.IsTrue(MyDrive.DriveType == DriveType.Fixed, "DriveType failed drive not of Fixed type");
mDebugger.Send("END TEST");
mDebugger.Send("START TEST: Getting all drives info");
DriveInfo[] xDrives = DriveInfo.GetDrives();
Assert.IsFalse(xDrives == null, "GetDrives failed: null array returned");
// I suppose that at least a drive is recognized by Cosmos
Assert.IsTrue(xDrives.Length > 0, "GetDrives failed: no drive recognized");
/* Check that only the Name property of the first one is the same of driveName */
Assert.IsTrue(xDrives[0].Name == driveName, "GetDrives failed: first drive is not the same of MyDrive (0:)");
mDebugger.Send("END TEST");
}
}
}

View file

@ -42,6 +42,7 @@ namespace Cosmos.Kernel.Tests.Fat2
mDebugger.Send("Run");
FileTest.Execute(mDebugger);
DriveInfoTest.Execute(mDebugger);
TestController.Completed();
}

View file

@ -19,12 +19,9 @@ namespace Cosmos.System.FileSystem
public class CosmosVFS : VFSBase
{
private List<Partition> mPartitions;
private List<FileSystem> mFileSystems;
private FileSystem mCurrentFileSystem;
private List<FileSystemFactory> mRegisteredFileSystems;
/// <summary>
/// Initializes the virtual file system.
@ -33,6 +30,9 @@ namespace Cosmos.System.FileSystem
{
mPartitions = new List<Partition>();
mFileSystems = new List<FileSystem>();
mRegisteredFileSystems = new List<FileSystemFactory>();
RegisterFileSystem(new FatFileSystemFactory());
InitializePartitions();
if (mPartitions.Count > 0)
@ -41,6 +41,12 @@ namespace Cosmos.System.FileSystem
}
}
public override void RegisterFileSystem(FileSystemFactory aFileSystemFactory)
{
Global.mFileSystemDebugger.SendInternal($"Registering filesystem {aFileSystemFactory.Name}");
mRegisteredFileSystems.Add(aFileSystemFactory);
}
/// <summary>
/// Creates a new file.
/// </summary>
@ -400,14 +406,15 @@ namespace Cosmos.System.FileSystem
{
string xRootPath = string.Concat(i, VolumeSeparatorChar, DirectorySeparatorChar);
var xSize = (long)(mPartitions[i].BlockCount * mPartitions[i].BlockSize / 1024 / 1024);
switch (FileSystem.GetFileSystemType(mPartitions[i]))
// We 'probe' the partition <i> with all the FileSystem registered until we find a Filesystem that can read / write to it
foreach (var fs in mRegisteredFileSystems)
{
case FileSystemType.FAT:
if (fs.IsType(mPartitions[i]))
{
Global.mFileSystemDebugger.SendInternal($"Partion {i} has a {fs.Name} filesystem");
mFileSystems.Add(new FatFileSystem(mPartitions[i], xRootPath, xSize));
break;
default:
global::System.Console.WriteLine("Unknown filesystem type!");
return;
}
}
if ((mFileSystems.Count > 0) && (mFileSystems[mFileSystems.Count - 1].mRootPath == xRootPath))
@ -547,5 +554,70 @@ namespace Cosmos.System.FileSystem
return aFS.GetRootDirectory();
}
/// <summary>
/// Verifies if driveId is a valid id for a drive.
/// </summary>
/// <param name="driveId">The id of the drive.</param>
/// <returns>true if the drive id is valid, false otherwise.</returns>
public override bool IsValidDriveId(string driveId)
{
Global.mFileSystemDebugger.SendInternal($"driveId is {driveId} after normalization");
/* We need to remove ':\' to get only the numeric value */
driveId = driveId.Remove(driveId.Length - 2);
Global.mFileSystemDebugger.SendInternal($"driveId is now {driveId}");
/*
* Cosmos Drive name is really similar to DOS / Windows but a number instead of a letter is used, it is not limited
* to 1 character but any number is valid
*/
bool isOK = Int32.TryParse(driveId, out int val);
Global.mFileSystemDebugger.SendInternal($"isOK is {isOK}");
return isOK;
}
public override long GetTotalSize(string aDriveId)
{
var xFs = GetFileSystemFromPath(aDriveId);
return xFs.mSize;
}
public override long GetAvailableFreeSpace(string aDriveId)
{
var xFs = GetFileSystemFromPath(aDriveId);
return xFs.mAvailableFreeSpace;
}
public override long GetTotalFreeSpace(string aDriveId)
{
var xFs = GetFileSystemFromPath(aDriveId);
return xFs.mTotalFreeSpace;
}
public override string GetFileSystemType(string aDriveId)
{
var xFs = GetFileSystemFromPath(aDriveId);
return xFs.mType;
}
public override string GetFileSystemLabel(string aDriveId)
{
var xFs = GetFileSystemFromPath(aDriveId);
return xFs.mLabel;
}
public override void SetFileSystemLabel(string aDriveId, string aLabel)
{
var xFs = GetFileSystemFromPath(aDriveId);
xFs.mLabel = aLabel;
}
}
}

View file

@ -353,6 +353,24 @@ namespace Cosmos.System.FileSystem.FAT
private readonly Fat[] mFats;
public override string mType
{
get
{
switch (mFatType)
{
case FatTypeEnum.Fat12:
return "FAT12";
case FatTypeEnum.Fat16:
return "FAT16";
case FatTypeEnum.Fat32:
return "FAT32";
default:
throw new Exception("Unknown FAT file system type.");
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FatFileSystem"/> class.
/// </summary>
@ -587,7 +605,7 @@ namespace Cosmos.System.FileSystem.FAT
}
var result = new List<DirectoryEntry>();
var xEntry = (FatDirectoryEntry)baseDirectory;
var xEntry = (FatDiretoryEntry)baseDirectory;
var fatListing = xEntry.ReadDirectoryContents();
for (int i = 0; i < fatListing.Count; i++)
@ -601,7 +619,7 @@ namespace Cosmos.System.FileSystem.FAT
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.GetRootDirectory --");
var xRootEntry = new FatDirectoryEntry(this, null, mRootPath, mSize, mRootPath, RootCluster);
var xRootEntry = new FatDiretoryEntry(this, null, mRootPath, mSize, mRootPath, RootCluster);
return xRootEntry;
}
@ -623,7 +641,7 @@ namespace Cosmos.System.FileSystem.FAT
throw new ArgumentNullException(nameof(aNewDirectory));
}
var xParentDirectory = (FatDirectoryEntry)aParentDirectory;
var xParentDirectory = (FatDiretoryEntry)aParentDirectory;
var xDirectoryEntryToAdd = xParentDirectory.AddDirectoryEntry(aNewDirectory, DirectoryEntryTypeEnum.Directory);
return xDirectoryEntryToAdd;
}
@ -646,7 +664,8 @@ namespace Cosmos.System.FileSystem.FAT
throw new ArgumentNullException(nameof(aNewFile));
}
var xParentDirectory = (FatDirectoryEntry)aParentDirectory;
var xParentDirectory = (FatDiretoryEntry)aParentDirectory;
var xDirectoryEntryToAdd = xParentDirectory.AddDirectoryEntry(aNewFile, DirectoryEntryTypeEnum.File);
return xDirectoryEntryToAdd;
}
@ -658,7 +677,7 @@ namespace Cosmos.System.FileSystem.FAT
throw new ArgumentNullException(nameof(aDirectoryEntry));
}
var xDirectoryEntry = (FatDirectoryEntry)aDirectoryEntry;
var xDirectoryEntry = (FatDiretoryEntry)aDirectoryEntry;
xDirectoryEntry.DeleteDirectoryEntry();
}
@ -670,7 +689,7 @@ namespace Cosmos.System.FileSystem.FAT
throw new ArgumentNullException(nameof(aDirectoryEntry));
}
var xDirectoryEntry = (FatDirectoryEntry)aDirectoryEntry;
var xDirectoryEntry = (FatDiretoryEntry)aDirectoryEntry;
var entries = xDirectoryEntry.GetFatTable();
@ -682,6 +701,64 @@ namespace Cosmos.System.FileSystem.FAT
xDirectoryEntry.DeleteDirectoryEntry();
}
public override string mLabel
{
/*
* In the FAT filesystem the name field of RootDirectory is - in reality - the Volume Label
*/
get
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.mLabel --");
var RootDirectory = (FatDiretoryEntry) GetRootDirectory();
var VolumeId = RootDirectory.FindVolumeId();
if (VolumeId == null)
{
Global.mFileSystemDebugger.SendInternal("No VolumeID, returning drive name");
return RootDirectory.mName;
}
Global.mFileSystemDebugger.SendInternal($"Volume label is {VolumeId.mName}");
return VolumeId.mName;
}
set
{
Global.mFileSystemDebugger.SendInternal($"Setting Volume label to {value}");
var RootDirectory = (FatDiretoryEntry) GetRootDirectory();
var VolumeId = RootDirectory.FindVolumeId();
if (VolumeId != null)
{
VolumeId.SetName(value);
return;
}
Global.mFileSystemDebugger.SendInternal("No VolumeID found, let's create it!");
VolumeId = RootDirectory.CreateVolumeId(value);
}
}
public override long mAvailableFreeSpace
{
get
{
var RootDirectory = (FatDiretoryEntry)GetRootDirectory();
// We do not support "user quotas" for now so this is effectively the same then mTotalFreeSpace
return mSize - RootDirectory.GetUsedSpace();
}
}
public override long mTotalFreeSpace
{
get
{
var RootDirectory = (FatDiretoryEntry)GetRootDirectory();
return mSize - RootDirectory.GetUsedSpace();
}
}
private enum FatTypeEnum
{
Unknown,

View file

@ -15,7 +15,7 @@ namespace Cosmos.System.FileSystem.FAT
protected long mPosition;
private readonly FatDirectoryEntry mDirectoryEntry;
private readonly FatDiretoryEntry mDirectoryEntry;
private readonly FatFileSystem mFS;
@ -29,7 +29,7 @@ namespace Cosmos.System.FileSystem.FAT
private long mSize;
public FatStream(FatDirectoryEntry aEntry)
public FatStream(FatDiretoryEntry aEntry)
{
if (aEntry == null)
{

View file

@ -10,7 +10,7 @@ using Cosmos.System.FileSystem.Listing;
namespace Cosmos.System.FileSystem.FAT.Listing
{
internal class FatDirectoryEntry : DirectoryEntry
internal class FatDiretoryEntry : DirectoryEntry
{
private readonly uint mEntryHeaderDataOffset;
@ -18,9 +18,9 @@ namespace Cosmos.System.FileSystem.FAT.Listing
// Size is UInt32 because FAT doesn't support bigger.
// Don't change to UInt64
public FatDirectoryEntry(
public FatDiretoryEntry(
FatFileSystem aFileSystem,
FatDirectoryEntry aParent,
FatDiretoryEntry aParent,
string aFullPath,
string aName,
long aSize,
@ -31,6 +31,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
{
if (aFirstCluster < aFileSystem.RootCluster)
{
Global.mFileSystemDebugger.SendInternal($"aFirstCluster {aFirstCluster} < aFileSystem.RootCluster {aFileSystem.RootCluster}");
throw new ArgumentOutOfRangeException(nameof(aFirstCluster));
}
@ -38,9 +39,9 @@ namespace Cosmos.System.FileSystem.FAT.Listing
mEntryHeaderDataOffset = aEntryHeaderDataOffset;
}
public FatDirectoryEntry(
public FatDiretoryEntry(
FatFileSystem aFileSystem,
FatDirectoryEntry aParent,
FatDiretoryEntry aParent,
string aFullPath,
long aSize,
string aName,
@ -143,7 +144,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
GetFatTable();
}
public FatDirectoryEntry AddDirectoryEntry(string aName, DirectoryEntryTypeEnum aEntryType)
public FatDiretoryEntry AddDirectoryEntry(string aName, DirectoryEntryTypeEnum aEntryType)
{
Global.mFileSystemDebugger.SendInternal("-- FatDirectoryEntry.AddDirectoryEntry --");
Global.mFileSystemDebugger.SendInternal("aName =");
@ -203,7 +204,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
}
int n = 1;
List<FatDirectoryEntry> xDirectoryEntries = ReadDirectoryContents(true);
List<FatDiretoryEntry> xDirectoryEntries = ReadDirectoryContents(true);
string[] xShortFilenames = new string[xDirectoryEntries.Count];
for (int i = 0; i < xDirectoryEntries.Count; i++)
@ -279,7 +280,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
Global.mFileSystemDebugger.SendInternal("xEntryHeaderDataOffset =");
Global.mFileSystemDebugger.SendInternal(xEntryHeaderDataOffset);
var xNewEntry = new FatDirectoryEntry((FatFileSystem)mFileSystem, this, xFullPath, aName, 0, xFirstCluster, xEntryHeaderDataOffset, aEntryType);
var xNewEntry = new FatDiretoryEntry((FatFileSystem)mFileSystem, this, xFullPath, aName, 0, xFirstCluster, xEntryHeaderDataOffset, aEntryType);
xNewEntry.AllocateDirectoryEntry(xShortName);
@ -289,6 +290,8 @@ namespace Cosmos.System.FileSystem.FAT.Listing
throw new ArgumentOutOfRangeException(nameof(aEntryType), "Unknown directory entry type.");
}
private bool IsRootDirectory() => (mParent == null) ? true : false;
public void DeleteDirectoryEntry()
{
if (mEntryType == DirectoryEntryTypeEnum.Unknown)
@ -296,25 +299,23 @@ namespace Cosmos.System.FileSystem.FAT.Listing
throw new NotImplementedException();
}
if (mParent != null)
if (IsRootDirectory())
{
var xData = ((FatDirectoryEntry)mParent).GetDirectoryEntryData();
var xEntryOffset = mEntryHeaderDataOffset - 32;
while (xData[xEntryOffset + 11] == FatDirectoryEntryAttributeConsts.LongName)
{
xData[xEntryOffset] = FatDirectoryEntryAttributeConsts.UnusedOrDeletedEntry;
xEntryOffset -= 32;
}
((FatDirectoryEntry)mParent).SetDirectoryEntryData(xData);
throw new Exception("Root directory can not be deleted");
}
else
var xData = ((FatDiretoryEntry)mParent).GetDirectoryEntryData();
var xEntryOffset = mEntryHeaderDataOffset - 32;
while (xData[xEntryOffset + 11] == FatDirectoryEntryAttributeConsts.LongName)
{
throw new Exception("Parent directory is null");
xData[xEntryOffset] = FatDirectoryEntryAttributeConsts.UnusedOrDeletedEntry;
xEntryOffset -= 32;
}
((FatDiretoryEntry)mParent).SetDirectoryEntryData(xData);
SetDirectoryEntryMetadataValue(FatDirectoryEntryMetadata.FirstByte, FatDirectoryEntryAttributeConsts.UnusedOrDeletedEntry);
// GetFatTable calls GetFatChain, which "refreshes" the FAT table and clusters
@ -322,16 +323,16 @@ namespace Cosmos.System.FileSystem.FAT.Listing
}
/// <summary>
/// Retrieves a <see cref="List{T}"/> of <see cref="FatDirectoryEntry"/> objects that represent the Directory Entries inside this Directory
/// Retrieves a <see cref="List{T}"/> of <see cref="FatDiretoryEntry"/> objects that represent the Directory Entries inside this Directory
/// </summary>
/// <returns>Returns a <see cref="List{T}"/> of the Directory Entries inside this Directory</returns>
public List<FatDirectoryEntry> ReadDirectoryContents(bool aReturnShortFilenames = false)
public List<FatDiretoryEntry> ReadDirectoryContents(bool aReturnShortFilenames = false)
{
Global.mFileSystemDebugger.SendInternal("-- FatDirectoryEntry.ReadDirectoryContents --");
var xData = GetDirectoryEntryData();
var xResult = new List<FatDirectoryEntry>();
FatDirectoryEntry xParent = this;
var xResult = new List<FatDiretoryEntry>();
FatDiretoryEntry xParent = this;
//TODO: Change xLongName to StringBuilder
string xLongName = "";
@ -474,7 +475,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
}
string xFullPath = Path.Combine(mFullPath, xName);
var xEntry = new FatDirectoryEntry(((FatFileSystem)mFileSystem), xParent, xFullPath, xName, xSize, xFirstCluster, i, DirectoryEntryTypeEnum.File);
var xEntry = new FatDiretoryEntry(((FatFileSystem)mFileSystem), xParent, xFullPath, xName, xSize, xFirstCluster, i, DirectoryEntryTypeEnum.File);
xResult.Add(xEntry);
Global.mFileSystemDebugger.SendInternal(xEntry.mName + " - " + xEntry.mSize + " bytes");
}
@ -482,7 +483,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
{
string xFullPath = Path.Combine(mFullPath, xName);
uint xSize = xData.ToUInt32(i + 28);
var xEntry = new FatDirectoryEntry(((FatFileSystem)mFileSystem), xParent, xFullPath, xName, xSize, xFirstCluster, i, DirectoryEntryTypeEnum.Directory);
var xEntry = new FatDiretoryEntry(((FatFileSystem)mFileSystem), xParent, xFullPath, xName, xSize, xFirstCluster, i, DirectoryEntryTypeEnum.Directory);
Global.mFileSystemDebugger.SendInternal(xEntry.mName + " <DIR> " + xEntry.mSize + " bytes : Attrib = " + xAttrib + ", Status = " + xStatus);
xResult.Add(xEntry);
}
@ -502,6 +503,62 @@ namespace Cosmos.System.FileSystem.FAT.Listing
return xResult;
}
public FatDiretoryEntry FindVolumeId()
{
if (!IsRootDirectory())
{
throw new Exception("VolumeId can be found only in Root Directory");
}
Global.mFileSystemDebugger.SendInternal("-- FatDirectoryEntry.FindVolumeId --");
var xData = GetDirectoryEntryData();
FatDiretoryEntry xParent = this;
FatDiretoryEntry xResult = null;
for (uint i = 0; i < xData.Length; i = i + 32)
{
byte xAttrib = xData[i + 11];
//if ((xAttrib & FatDirectoryEntryAttributeConsts.VolumeID) != FatDirectoryEntryAttributeConsts.VolumeID)
if (xAttrib != FatDirectoryEntryAttributeConsts.VolumeID)
continue;
Global.mFileSystemDebugger.SendInternal("VolumeID Found");
/* The Label in FAT could be only a shortName (limited to 11 characters) so it is more easy */
string xName = xData.GetAsciiString(i, 11).TrimEnd();
string xFullPath = Path.Combine(mFullPath, xName);
/* Probably can be OK to hardcode 0 here */
uint xSize = xData.ToUInt32(i + 28);
//uint xFirstCluster = (uint)(xData.ToUInt16(i + 20) << 16 | xData.ToUInt16(i + 26));
uint xFirstCluster = xParent.mFirstClusterNum;
Global.mFileSystemDebugger.SendInternal($"VolumeID Found xName {xName} xFullPath {xFullPath} xSize {xSize} xFirstCluster {xFirstCluster}");
xResult = new FatDiretoryEntry(((FatFileSystem)mFileSystem), xParent, xFullPath, xName, xSize, xFirstCluster, i, DirectoryEntryTypeEnum.File);
break;
}
if (xResult == null)
Global.mFileSystemDebugger.SendInternal($"VolumeID not found, returning null");
return xResult;
}
public FatDiretoryEntry CreateVolumeId(string name)
{
if (!IsRootDirectory())
{
throw new Exception("VolumeId can be created only in Root Directory");
}
// VolumeId is really a special type of File with attribute 'VolumeID' set
var VolumeId = AddDirectoryEntry(name, DirectoryEntryTypeEnum.File);
VolumeId.SetDirectoryEntryMetadataValue(FatDirectoryEntryMetadata.Attributes, FatDirectoryEntryAttributeConsts.VolumeID);
return VolumeId;
}
/// <summary>
/// Tries to find an empty space for a directory entry and returns the offset to that space if successful, otherwise throws an exception.
/// </summary>
@ -614,23 +671,21 @@ namespace Cosmos.System.FileSystem.FAT.Listing
Global.mFileSystemDebugger.SendInternal("aValue =");
Global.mFileSystemDebugger.SendInternal(aValue);
if (mParent != null)
{
var xData = ((FatDirectoryEntry)mParent).GetDirectoryEntryData();
if (xData.Length > 0)
{
var xValue = new byte[aEntryMetadata.DataLength];
xValue.SetUInt32(0, aValue);
uint offset = mEntryHeaderDataOffset + aEntryMetadata.DataOffset;
Array.Copy(xValue, 0, xData, offset, aEntryMetadata.DataLength);
((FatDirectoryEntry)mParent).SetDirectoryEntryData(xData);
}
}
else
if (IsRootDirectory())
{
throw new Exception("Root directory metadata can not be changed using the file stream.");
}
var xData = ((FatDiretoryEntry)mParent).GetDirectoryEntryData();
if (xData.Length > 0)
{
var xValue = new byte[aEntryMetadata.DataLength];
xValue.SetUInt32(0, aValue);
uint offset = mEntryHeaderDataOffset + aEntryMetadata.DataOffset;
Array.Copy(xValue, 0, xData, offset, aEntryMetadata.DataLength);
((FatDiretoryEntry)mParent).SetDirectoryEntryData(xData);
}
}
internal void SetDirectoryEntryMetadataValue(FatDirectoryEntryMetadata aEntryMetadata, long aValue)
@ -639,25 +694,23 @@ namespace Cosmos.System.FileSystem.FAT.Listing
Global.mFileSystemDebugger.SendInternal("aValue =");
Global.mFileSystemDebugger.SendInternal(aValue);
if (mParent != null)
{
var xData = ((FatDirectoryEntry)mParent).GetDirectoryEntryData();
if (xData.Length > 0)
{
var xValue = new byte[aEntryMetadata.DataLength];
xValue.SetUInt32(0, (uint)aValue);
uint offset = mEntryHeaderDataOffset + aEntryMetadata.DataOffset;
Global.mFileSystemDebugger.SendInternal("offset =");
Global.mFileSystemDebugger.SendInternal(offset);
Array.Copy(xValue, 0, xData, offset, aEntryMetadata.DataLength);
((FatDirectoryEntry)mParent).SetDirectoryEntryData(xData);
}
}
else
if (IsRootDirectory())
{
throw new Exception("Root directory metadata can not be changed using the file stream.");
}
var xData = ((FatDiretoryEntry)mParent).GetDirectoryEntryData();
if (xData.Length > 0)
{
var xValue = new byte[aEntryMetadata.DataLength];
xValue.SetUInt32(0, (uint)aValue);
uint offset = mEntryHeaderDataOffset + aEntryMetadata.DataOffset;
Global.mFileSystemDebugger.SendInternal("offset =");
Global.mFileSystemDebugger.SendInternal(offset);
Array.Copy(xValue, 0, xData, offset, aEntryMetadata.DataLength);
((FatDiretoryEntry)mParent).SetDirectoryEntryData(xData);
}
}
internal void SetDirectoryEntryMetadataValue(FatDirectoryEntryMetadata aEntryMetadata, string aValue)
@ -666,7 +719,12 @@ namespace Cosmos.System.FileSystem.FAT.Listing
Global.mFileSystemDebugger.SendInternal("aValue =");
Global.mFileSystemDebugger.SendInternal(aValue);
var xData = ((FatDirectoryEntry)mParent).GetDirectoryEntryData();
if (IsRootDirectory())
{
throw new Exception("Root directory metadata can not be changed using the file stream.");
}
var xData = ((FatDiretoryEntry)mParent).GetDirectoryEntryData();
if (xData.Length > 0)
{
@ -676,7 +734,7 @@ namespace Cosmos.System.FileSystem.FAT.Listing
uint offset = mEntryHeaderDataOffset + aEntryMetadata.DataOffset;
Array.Copy(xValue, 0, xData, offset, aEntryMetadata.DataLength);
((FatDirectoryEntry)mParent).SetDirectoryEntryData(xData);
((FatDiretoryEntry)mParent).SetDirectoryEntryData(xData);
}
}
@ -791,5 +849,100 @@ namespace Cosmos.System.FileSystem.FAT.Listing
return xChecksum;
}
private long GetDirectoryEntrySize(byte[] DirectoryEntryData)
{
long xResult = 0;
for (uint i = 0; i < DirectoryEntryData.Length; i = i + 32)
{
byte xAttrib = DirectoryEntryData[i + 11];
byte xStatus = DirectoryEntryData[i];
if (xAttrib == FatDirectoryEntryAttributeConsts.LongName)
{
//Global.mFileSystemDebugger.SendInternal($"-- FatDirectoryEntry.GetDirectoryEntrySize() LongName DirEntry skipped!");
continue;
}
if (xStatus == 0x00)
{
//Global.mFileSystemDebugger.SendInternal("<EOF> : Attrib = " + xAttrib + ", Status = " + xStatus);
break;
}
switch (xStatus)
{
case 0x05:
// Japanese characters - We dont handle these
continue;
case 0x2E:
// Dot entry
continue;
case FatDirectoryEntryAttributeConsts.UnusedOrDeletedEntry:
// Empty slot, skip it
continue;
default:
break;
}
int xTest = xAttrib & (FatDirectoryEntryAttributeConsts.Directory | FatDirectoryEntryAttributeConsts.VolumeID);
switch (xTest)
{
// Normal file
case 0:
uint xSize = DirectoryEntryData.ToUInt32(i + 28);
xResult += xSize;
break;
case FatDirectoryEntryAttributeConsts.Directory:
//Global.mFileSystemDebugger.SendInternal($"-- FatDirectoryEntry.GetDirectoryEntrySize() found directory: recursing!");
uint xFirstCluster = (uint)(DirectoryEntryData.ToUInt16(i + 20) << 16 | DirectoryEntryData.ToUInt16(i + 26));
byte[] xDirData;
((FatFileSystem)mFileSystem).Read(xFirstCluster, out xDirData);
xResult += GetDirectoryEntrySize(xDirData);
break;
case FatDirectoryEntryAttributeConsts.VolumeID:
//Global.mFileSystemDebugger.SendInternal("<VOLUME ID>: skipped");
continue;
default:
//Global.mFileSystemDebugger.SendInternal("<INVALID ENTRY>: skipped");
continue;
}
}
//Global.mFileSystemDebugger.SendInternal($"-- FatDirectoryEntry.GetDirectoryEntrySize() is {xResult} bytes");
return xResult;
}
/*
* Please note that this could become slower and slower as the partion becomes greater this could be optimized in two ways:
* 1. Compute the value using this function on FS inizialization and write the difference between TotalSpace and the computed
* value to the specif field of 'FS Information Sector' of FAT32
* 2. Compute the value using this function on FS inizialization and write the difference between TotalSpace and the computed
* value in a sort of memory cache in VFS itself
*
* In any case if one of this two methods will be used in the future when a file is removed or new data are written on it,
* the value on the field should be always updated.
*/
public override long GetUsedSpace()
{
Global.mFileSystemDebugger.SendInternal($"-- FatDirectoryEntry.GetUsedSpace() on Directory {mName} ---");
long xResult = 0;
var xData = GetDirectoryEntryData();
xResult += GetDirectoryEntrySize(xData);
Global.mFileSystemDebugger.SendInternal($"-- FatDirectoryEntry.GetUsedSpace() is {xResult} bytes");
return xResult;
}
}
}

View file

@ -0,0 +1,23 @@
using Cosmos.HAL.BlockDevice;
using Cosmos.System.FileSystem.FAT;
using System;
using System.Collections.Generic;
using System.Text;
namespace Cosmos.System.FileSystem
{
public class FatFileSystemFactory : FileSystemFactory
{
public override string Name { get => "FAT"; }
/// <summary>
/// Initializes a new instance of the <see cref="FatFileSystem"/> class.
/// </summary>
/// <param name="aDevice">The partition.</param>
/// <param name="aRootPath">The root path.</param>
/// <exception cref="Exception">FAT signature not found.</exception>
public override FileSystem Create(Partition aDevice, string aRootPath, long aSize) => new FatFileSystem(aDevice, aRootPath, aSize);
public override bool IsType(Partition aDevice) => FatFileSystem.IsDeviceFat(aDevice);
}
}

View file

@ -16,16 +16,6 @@ namespace Cosmos.System.FileSystem
mSize = aSize;
}
public static FileSystemType GetFileSystemType(Partition aDevice)
{
if (FatFileSystem.IsDeviceFat(aDevice))
{
return FileSystemType.FAT;
}
return FileSystemType.Unknown;
}
public abstract void DisplayFileSystemInfo();
public abstract List<DirectoryEntry> GetDirectoryListing(DirectoryEntry baseDirectory);
@ -45,5 +35,13 @@ namespace Cosmos.System.FileSystem
public string mRootPath { get; }
public long mSize { get; }
public abstract long mAvailableFreeSpace { get; }
public abstract long mTotalFreeSpace { get; }
public abstract string mType { get; }
public abstract string mLabel { get; set; }
}
}

View file

@ -0,0 +1,16 @@
using Cosmos.HAL.BlockDevice;
using System;
using System.Collections.Generic;
using System.Text;
namespace Cosmos.System.FileSystem
{
public class FileSystemFactory
{
public virtual string Name { get; private set; }
public virtual FileSystem Create(Partition aDevice, string aRootPath, long aSize) => null;
public virtual bool IsType(Partition aDevice) => false;
}
}

View file

@ -2,7 +2,7 @@
{
public enum FileSystemType
{
Unknown = 0,
FAT,
Unknown
}
}
}

View file

@ -79,5 +79,7 @@ namespace Cosmos.System.FileSystem.Listing
public abstract void SetSize(long aSize);
public abstract Stream GetFileStream();
public abstract long GetUsedSpace();
}
}
}

View file

@ -8,6 +8,8 @@ namespace Cosmos.System.FileSystem.VFS
{
public abstract void Initialize();
public abstract void RegisterFileSystem(FileSystemFactory aFileSystemFactory);
public abstract DirectoryEntry CreateFile(string aPath);
public abstract DirectoryEntry CreateDirectory(string aPath);
@ -37,5 +39,19 @@ namespace Cosmos.System.FileSystem.VFS
public static char AltDirectorySeparatorChar { get { return '/'; } }
public static char VolumeSeparatorChar { get { return ':'; } }
public abstract bool IsValidDriveId(string driveId);
public abstract long GetTotalSize(string aDriveId);
public abstract long GetAvailableFreeSpace(string aDriveId);
public abstract long GetTotalFreeSpace(string aDriveId);
public abstract string GetFileSystemType(string aDriveId);
public abstract string GetFileSystemLabel(string aDriveId);
public abstract void SetFileSystemLabel(string aDriveId, string aLabel);
}
}

View file

@ -201,19 +201,20 @@ namespace Cosmos.System.FileSystem.VFS
return mVFS.GetVolumes();
}
public static void RegisterFileSystem(FileSystemFactory aFileSystemFactory)
{
mVFS.RegisterFileSystem(aFileSystemFactory);
}
public static List<string> GetLogicalDrives()
{
Global.mFileSystemDebugger.SendInternal("--- VFSManager.GetLogicalDrives ---");
//TODO: Directory.GetLogicalDrives() will call this.
return null;
/*
List<string> xDrives = new List<string>();
foreach (FilesystemEntry entry in GetVolumes())
xDrives.Add(entry.Name + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar);
return xDrives.ToArray();
*/
foreach (DirectoryEntry entry in GetVolumes())
xDrives.Add(entry.mName + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar);
return xDrives;
}
public static List<string> InternalGetFileDirectoryNames(
@ -430,6 +431,43 @@ namespace Cosmos.System.FileSystem.VFS
Global.mFileSystemDebugger.SendInternal("--- VFSManager.GetFileAttributes ---");
mVFS.SetFileAttributes(aPath, fileAttributes);
}
public static bool IsValidDriveId(string aPath)
{
Global.mFileSystemDebugger.SendInternal("--- VFSManager.GetFileAttributes ---");
return mVFS.IsValidDriveId(aPath);
}
public static long GetTotalSize(string aDriveId)
{
return mVFS.GetTotalSize(aDriveId);
}
public static long GetAvailableFreeSpace(string aDriveId)
{
return mVFS.GetAvailableFreeSpace(aDriveId);
}
public static long GetTotalFreeSpace(string aDriveId)
{
return mVFS.GetTotalFreeSpace(aDriveId);
}
public static string GetFileSystemType(string aDriveId)
{
return mVFS.GetFileSystemType(aDriveId);
}
public static string GetFileSystemLabel(string aDriveId)
{
return mVFS.GetFileSystemLabel(aDriveId);
}
public static void SetFileSystemLabel(string aDriveId, string aLabel)
{
mVFS.SetFileSystemLabel(aDriveId, aLabel);
}
#region Helpers
public static char GetAltDirectorySeparatorChar()

View file

@ -0,0 +1,115 @@
//#define COSMOSDEBUG
using Cosmos.System;
using Cosmos.System.FileSystem;
using Cosmos.System.FileSystem.VFS;
using IL2CPU.API.Attribs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cosmos.System_Plugs.System.IO
{
[Plug(Target = typeof(DriveInfo))]
public static class DriveInfoImpl
{
public static string NormalizeDriveName(string driveName)
{
string name;
if (driveName.Length == 1)
name = driveName + ":\\";
else
{
name = Path.GetPathRoot(driveName);
// Disallow null or empty drive letters and UNC paths
if (name == null || name.Length == 0 || name.StartsWith("\\\\", StringComparison.Ordinal))
throw new ArgumentException("Argument must be drive identifier or root dir");
}
// We want to normalize to have a trailing backslash so we don't have two equivalent forms and
// because some Win32 API don't work without it.
if (name.Length == 2 && name[1] == ':')
{
name = name + "\\";
}
if (!VFSManager.IsValidDriveId(name))
{
throw new ArgumentException("Argument must be drive identifier or root dir");
}
return name;
}
public static long get_AvailableFreeSpace(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting Available Free Space of {aThis.Name}");
return VFSManager.GetAvailableFreeSpace(aThis.Name);
}
public static long get_TotalFreeSpace(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting Total Free Space of {aThis.Name}");
return VFSManager.GetTotalFreeSpace(aThis.Name);
}
public static long get_TotalSize(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting size of {aThis.Name}");
return VFSManager.GetTotalSize(aThis.Name);
}
public static string get_DriveFormat(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting format of {aThis.Name}");
return VFSManager.GetFileSystemType(aThis.Name);
}
public static string get_VolumeLabel(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting label of {aThis.Name}");
return VFSManager.GetFileSystemLabel(aThis.Name);
}
public static void set_VolumeLabel(DriveInfo aThis, string aLabel)
{
Global.mFileSystemDebugger.SendInternal($"Setting label of {aThis.Name} with {aLabel}");
VFSManager.SetFileSystemLabel(aThis.Name, aLabel);
}
/* For now I'm forcing IsReady to be always true as only fixed drives are supported in Cosmos for now */
public static bool get_IsReady(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting isReady status of {aThis.Name}");
return true;
}
/* For now I'm forcing DriveType to always be 'Fixed' as only fixed drives are supported in Cosmos for now */
public static DriveType get_DriveType(DriveInfo aThis)
{
Global.mFileSystemDebugger.SendInternal($"Getting DriveType of {aThis.Name}");
return DriveType.Fixed;
}
public static DriveInfo[] GetDrives()
{
Global.mFileSystemDebugger.SendInternal("GetDrives called");
List<string> drives = VFSManager.GetLogicalDrives();
DriveInfo[] result = new DriveInfo[drives.Count];
for (int i = 0; i < drives.Count; i++)
{
result[i] = new DriveInfo(drives[i]);
}
return result;
}
}
}

View file

@ -46,5 +46,20 @@ namespace Cosmos.System_Plugs.System
return result;
}
/* .Net Core TryParse is calling Number.TryParse() that does NRE in Cosmos, plugged it for now */
public static bool TryParse(string s, out int result)
{
try
{
result = Int32.Parse(s);
return true;
}
catch
{
result = 0;
return false;
}
}
}
}