//#define COSMOSDEBUG using System; using System.Collections.Generic; using System.IO; using Cosmos.HAL.BlockDevice; using Cosmos.System.FileSystem.FAT; using Cosmos.System.FileSystem.Listing; using Cosmos.System.FileSystem.VFS; namespace Cosmos.System.FileSystem { // ReSharper disable once InconsistentNaming /// /// Cosmos default virtual file system. /// /// public class CosmosVFS : VFSBase { private List mPartitions; private List mFileSystems; private FileSystem mCurrentFileSystem; /// /// Initializes the virtual file system. /// public override void Initialize() { mPartitions = new List(); mFileSystems = new List(); InitializePartitions(); if (mPartitions.Count > 0) { InitializeFileSystems(); } } /// /// Creates a new file. /// /// The full path including the file to create. /// /// /// aPath public override DirectoryEntry CreateFile(string aPath) { Global.mFileSystemDebugger.SendInternal("--- CosmosVFS.CreateFile ---"); if (aPath == null) { throw new ArgumentNullException(nameof(aPath)); } if (aPath.Length == 0) { throw new ArgumentException("aPath"); } Global.mFileSystemDebugger.SendInternal("aPath ="); Global.mFileSystemDebugger.SendInternal(aPath); if (File.Exists(aPath)) { Global.mFileSystemDebugger.SendInternal("File already exists."); return GetFile(aPath); } Global.mFileSystemDebugger.SendInternal("File doesn't exist."); string xFileToCreate = Path.GetFileName(aPath); Global.mFileSystemDebugger.SendInternal("After GetFileName"); Global.mFileSystemDebugger.SendInternal("xFileToCreate ="); Global.mFileSystemDebugger.SendInternal(xFileToCreate); string xParentDirectory = aPath.Remove(aPath.Length - xFileToCreate.Length); Global.mFileSystemDebugger.SendInternal("After removing last path part"); Global.mFileSystemDebugger.SendInternal("xParentDirectory ="); Global.mFileSystemDebugger.SendInternal(xParentDirectory); DirectoryEntry xParentEntry = GetDirectory(xParentDirectory); if (xParentEntry == null) { Global.mFileSystemDebugger.SendInternal("Parent directory doesn't exist."); xParentEntry = CreateDirectory(xParentDirectory); } Global.mFileSystemDebugger.SendInternal("Parent directory exists."); var xFS = GetFileSystemFromPath(xParentDirectory); return xFS.CreateFile(xParentEntry, xFileToCreate); } /// /// Creates a directory. /// /// The full path including the directory to create. /// /// /// aPath public override DirectoryEntry CreateDirectory(string aPath) { Global.mFileSystemDebugger.SendInternal("-- CosmosVFS.CreateDirectory ---"); if (aPath == null) { throw new ArgumentNullException(nameof(aPath)); } if (aPath.Length == 0) { throw new ArgumentException("aPath"); } Global.mFileSystemDebugger.SendInternal("aPath ="); Global.mFileSystemDebugger.SendInternal(aPath); if (Directory.Exists(aPath)) { Global.mFileSystemDebugger.SendInternal("Path already exists."); return GetDirectory(aPath); } Global.mFileSystemDebugger.SendInternal("Path doesn't exist."); string xDirectoryToCreate = Path.GetFileName(aPath); Global.mFileSystemDebugger.SendInternal("After GetFileName"); Global.mFileSystemDebugger.SendInternal("xDirectoryToCreate ="); Global.mFileSystemDebugger.SendInternal(xDirectoryToCreate); string xParentDirectory = aPath.Remove(aPath.Length - xDirectoryToCreate.Length); Global.mFileSystemDebugger.SendInternal("After removing last path part"); Global.mFileSystemDebugger.SendInternal("xParentDirectory ="); Global.mFileSystemDebugger.SendInternal(xParentDirectory); DirectoryEntry xParentEntry = GetDirectory(xParentDirectory); if (xParentEntry == null) { Global.mFileSystemDebugger.SendInternal("Parent directory doesn't exist."); xParentEntry = CreateDirectory(xParentDirectory); } Global.mFileSystemDebugger.SendInternal("Parent directory exists."); var xFS = GetFileSystemFromPath(xParentDirectory); return xFS.CreateDirectory(xParentEntry, xDirectoryToCreate); } /// /// Gets the directory listing for a path. /// /// The full path. /// public override List GetDirectoryListing(string aPath) { var xFS = GetFileSystemFromPath(aPath); var xDirectory = DoGetDirectoryEntry(aPath, xFS); return xFS.GetDirectoryListing(xDirectory); } /// /// Gets the directory listing for a directory entry. /// /// The directory entry. /// public override List GetDirectoryListing(DirectoryEntry aDirectory) { DirectoryEntry xTempEntry = aDirectory; string xFullPath = ""; while (xTempEntry.mParent != null) { xFullPath = Path.Combine(xTempEntry.mName, xFullPath); xTempEntry = xTempEntry.mParent; } return GetDirectoryListing(xFullPath); } /// /// Gets the directory entry for a directory. /// /// The full path path. /// A directory entry for the directory. /// public override DirectoryEntry GetDirectory(string aPath) { try { var xFileSystem = GetFileSystemFromPath(aPath); var xEntry = DoGetDirectoryEntry(aPath, xFileSystem); if ((xEntry != null) && (xEntry.mEntryType == DirectoryEntryTypeEnum.Directory)) { return xEntry; } } catch (Exception) { return null; } throw new Exception(aPath + " was found, but is not a directory."); } /// /// Gets the directory entry for a file. /// /// The full path. /// A directory entry for the file. /// public override DirectoryEntry GetFile(string aPath) { try { var xFileSystem = GetFileSystemFromPath(aPath); var xEntry = DoGetDirectoryEntry(aPath, xFileSystem); if ((xEntry != null) && (xEntry.mEntryType == DirectoryEntryTypeEnum.File)) { return xEntry; } } catch (Exception) { return null; } throw new Exception(aPath + " was found, but is not a file."); } /// /// Gets the volumes for all registered file systems. /// /// A list of directory entries for all volumes. public override List GetVolumes() { List xVolumes = new List(); for (int i = 0; i < mFileSystems.Count; i++) { xVolumes.Add(GetVolume(mFileSystems[i])); } return xVolumes; } /// /// Gets the directory entry for a volume. /// /// The volume root path. /// A directory entry for the volume. public override DirectoryEntry GetVolume(string aPath) { if (string.IsNullOrEmpty(aPath)) { return null; } var xFileSystem = GetFileSystemFromPath(aPath); if (xFileSystem != null) { return GetVolume(xFileSystem); } return null; } /// /// Initializes the partitions for all block devices. /// protected virtual void InitializePartitions() { for (int i = 0; i < BlockDevice.Devices.Count; i++) { if (BlockDevice.Devices[i] is Partition) { mPartitions.Add((Partition)BlockDevice.Devices[i]); break; } } if (mPartitions.Count > 0) { for (int i = 0; i < mPartitions.Count; i++) { Global.mFileSystemDebugger.SendInternal("Partition #: "); Global.mFileSystemDebugger.SendInternal(i + 1); global::System.Console.WriteLine("Partition #: " + (i + 1)); Global.mFileSystemDebugger.SendInternal("Block Size:"); Global.mFileSystemDebugger.SendInternal(mPartitions[i].BlockSize); global::System.Console.WriteLine("Block Size: " + mPartitions[i].BlockSize + " bytes"); Global.mFileSystemDebugger.SendInternal("Block Count:"); Global.mFileSystemDebugger.SendInternal(mPartitions[i].BlockCount); global::System.Console.WriteLine("Block Count: " + mPartitions[i].BlockCount); Global.mFileSystemDebugger.SendInternal("Size:"); Global.mFileSystemDebugger.SendInternal(mPartitions[i].BlockCount * mPartitions[i].BlockSize / 1024 / 1024); global::System.Console.WriteLine("Size: " + mPartitions[i].BlockCount * mPartitions[i].BlockSize / 1024 / 1024 + " MB"); } } else { global::System.Console.WriteLine("No partitions found!"); } } /// /// Initializes the file system for all partitions. /// protected virtual void InitializeFileSystems() { for (int i = 0; i < mPartitions.Count; i++) { string xRootPath = string.Concat(i, VolumeSeparatorChar, DirectorySeparatorChar); switch (FileSystem.GetFileSystemType(mPartitions[i])) { case FileSystemType.FAT: mFileSystems.Add(new FatFileSystem(mPartitions[i], xRootPath)); break; default: global::System.Console.WriteLine("Unknown filesystem type!"); return; } if ((mFileSystems.Count > 0) && (mFileSystems[mFileSystems.Count - 1].mRootPath == xRootPath)) { string xMessage = string.Concat("Initialized ", mFileSystems.Count, "filesystem(s)..."); global::System.Console.WriteLine(xMessage); mFileSystems[i].DisplayFileSystemInfo(); Directory.SetCurrentDirectory(xRootPath); } else { string xMessage = string.Concat("No filesystem found on partition #", i); global::System.Console.WriteLine(xMessage); } } } /// /// Gets the file system from a path. /// /// The path. /// The file system for the path. /// Unable to determine filesystem for path: + aPath private FileSystem GetFileSystemFromPath(string aPath) { Global.mFileSystemDebugger.SendInternal("--- CosmosVFS.GetFileSystemFromPath ---"); if (String.IsNullOrEmpty(aPath)) { throw new ArgumentException("Argument is null or empty", nameof(aPath)); } Global.mFileSystemDebugger.SendInternal("aPath ="); Global.mFileSystemDebugger.SendInternal(aPath); string xPath = Path.GetPathRoot(aPath); Global.mFileSystemDebugger.SendInternal("xPath after GetPathRoot ="); Global.mFileSystemDebugger.SendInternal(xPath); if ((mCurrentFileSystem != null) && (xPath == mCurrentFileSystem.mRootPath)) { Global.mFileSystemDebugger.SendInternal("Returning current file system."); return mCurrentFileSystem; } for (int i = 0; i < mFileSystems.Count; i++) { if (mFileSystems[i].mRootPath == xPath) { Global.mFileSystemDebugger.SendInternal("Found filesystem."); mCurrentFileSystem = mFileSystems[i]; return mCurrentFileSystem; } } throw new Exception("Unable to determine filesystem for path: " + aPath); } /// /// Attempts to get a directory entry for a path in a file system. /// /// The path. /// The file system. /// A directory entry for the path. /// aFS /// Path part ' + xPathPart + ' not found! private DirectoryEntry DoGetDirectoryEntry(string aPath, FileSystem aFS) { Global.mFileSystemDebugger.SendInternal("--- CosmosVFS.DoGetDirectoryEntry ---"); if (String.IsNullOrEmpty(aPath)) { throw new ArgumentException("Argument is null or empty", nameof(aPath)); } if (aFS == null) { throw new ArgumentNullException(nameof(aFS)); } Global.mFileSystemDebugger.SendInternal("aPath ="); Global.mFileSystemDebugger.SendInternal(aPath); string[] xPathParts = VFSManager.SplitPath(aPath); DirectoryEntry xBaseDirectory = GetVolume(aFS); if (xPathParts.Length == 1) { Global.mFileSystemDebugger.SendInternal("Returning the volume."); return xBaseDirectory; } // start at index 1, because 0 is the volume for (int i = 1; i < xPathParts.Length; i++) { var xPathPart = xPathParts[i].ToLower(); var xPartFound = false; var xListing = aFS.GetDirectoryListing(xBaseDirectory); for (int j = 0; j < xListing.Count; j++) { var xListingItem = xListing[j]; string xListingItemName = xListingItem.mName.ToLower(); xPathPart = xPathPart.ToLower(); if (xListingItemName == xPathPart) { xBaseDirectory = xListingItem; xPartFound = true; } } if (!xPartFound) { throw new Exception("Path part '" + xPathPart + "' not found!"); } } return xBaseDirectory; } /// /// Gets the root directory entry for a volume in a file system. /// /// The file system containing the volume. /// A directory entry for the volume. private DirectoryEntry GetVolume(FileSystem aFS) { Global.mFileSystemDebugger.SendInternal("--- CosmosVFS.GetVolume ---"); if (aFS == null) { Global.mFileSystemDebugger.SendInternal("File system is null."); throw new ArgumentNullException(nameof(aFS)); } return aFS.GetRootDirectory(); } } }