214 lines
4.9 KiB
C#
214 lines
4.9 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace DevDrive;
|
|
|
|
/// <summary>
|
|
/// Manages the in-memory file system storage for the virtual drive.
|
|
/// </summary>
|
|
public class FloppyStorage
|
|
{
|
|
public const long DefaultCapacity = 1_474_560;
|
|
public const int SectorSize = 512;
|
|
public const int AllocationUnitSize = 512;
|
|
|
|
private readonly ConcurrentDictionary<string, FloppyFileNode> _files = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly object _lock = new();
|
|
|
|
public long Capacity { get; }
|
|
public long FreeSpace { get; }
|
|
|
|
public FloppyStorage() : this(DefaultCapacity, DefaultCapacity)
|
|
{
|
|
}
|
|
|
|
public FloppyStorage(long totalBytes, long freeBytes)
|
|
{
|
|
Capacity = totalBytes;
|
|
FreeSpace = freeBytes;
|
|
var root = new FloppyFileNode("\\", true);
|
|
_files["\\"] = root;
|
|
}
|
|
|
|
public FloppyFileNode? CreateSymlink(string path, string targetPath, bool isDirectory)
|
|
{
|
|
path = NormalizePath(path);
|
|
|
|
if (_files.ContainsKey(path))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var parentPath = GetParentPath(path);
|
|
if (!_files.ContainsKey(parentPath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var node = new FloppyFileNode(path, isDirectory)
|
|
{
|
|
SymlinkTarget = targetPath,
|
|
Attributes = (isDirectory ? FileAttributes.Directory : FileAttributes.Normal) | FileAttributes.ReparsePoint
|
|
};
|
|
_files[path] = node;
|
|
return node;
|
|
}
|
|
|
|
public static string NormalizePath(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
return "\\";
|
|
}
|
|
|
|
path = path.Replace('/', '\\');
|
|
if (!path.StartsWith('\\'))
|
|
{
|
|
path = "\\" + path;
|
|
}
|
|
|
|
while (path.Contains("\\\\"))
|
|
{
|
|
path = path.Replace("\\\\", "\\");
|
|
}
|
|
|
|
if (path.Length > 1 && path.EndsWith('\\'))
|
|
{
|
|
path = path.TrimEnd('\\');
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
public static string GetParentPath(string path)
|
|
{
|
|
path = NormalizePath(path);
|
|
if (path == "\\")
|
|
{
|
|
return "\\";
|
|
}
|
|
|
|
var lastSlash = path.LastIndexOf('\\');
|
|
return lastSlash <= 0 ? "\\" : path[..lastSlash];
|
|
}
|
|
|
|
public bool Exists(string path)
|
|
{
|
|
path = NormalizePath(path);
|
|
return _files.ContainsKey(path);
|
|
}
|
|
|
|
public FloppyFileNode? GetNode(string path)
|
|
{
|
|
path = NormalizePath(path);
|
|
_files.TryGetValue(path, out var node);
|
|
return node;
|
|
}
|
|
|
|
public FloppyFileNode? CreateNode(string path, bool isDirectory)
|
|
{
|
|
path = NormalizePath(path);
|
|
|
|
if (_files.ContainsKey(path))
|
|
{
|
|
return _files[path];
|
|
}
|
|
|
|
var parentPath = GetParentPath(path);
|
|
if (!_files.ContainsKey(parentPath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var node = new FloppyFileNode(path, isDirectory);
|
|
_files[path] = node;
|
|
return node;
|
|
}
|
|
|
|
public bool DeleteNode(string path)
|
|
{
|
|
path = NormalizePath(path);
|
|
|
|
if (path == "\\")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var node = GetNode(path);
|
|
if (node == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (node.IsDirectory)
|
|
{
|
|
var children = GetChildren(path);
|
|
if (children.Any())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return _files.TryRemove(path, out _);
|
|
}
|
|
|
|
public IEnumerable<FloppyFileNode> GetChildren(string path)
|
|
{
|
|
path = NormalizePath(path);
|
|
|
|
foreach (var kvp in _files)
|
|
{
|
|
var filePath = kvp.Key;
|
|
if (filePath == path)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var parentPath = GetParentPath(filePath);
|
|
if (string.Equals(parentPath, path, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
yield return kvp.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool MoveNode(string oldPath, string newPath, bool replace)
|
|
{
|
|
oldPath = NormalizePath(oldPath);
|
|
newPath = NormalizePath(newPath);
|
|
|
|
if (!_files.TryGetValue(oldPath, out var node))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_files.ContainsKey(newPath))
|
|
{
|
|
if (!replace)
|
|
{
|
|
return false;
|
|
}
|
|
_files.TryRemove(newPath, out _);
|
|
}
|
|
|
|
var newParentPath = GetParentPath(newPath);
|
|
if (!_files.ContainsKey(newParentPath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_files.TryRemove(oldPath, out _);
|
|
|
|
node.FullPath = newPath;
|
|
node.Name = Path.GetFileName(newPath);
|
|
node.LastWriteTime = DateTime.Now;
|
|
|
|
_files[newPath] = node;
|
|
return true;
|
|
}
|
|
|
|
public bool HasEnoughSpace(long additionalBytes)
|
|
{
|
|
return FreeSpace >= additionalBytes;
|
|
}
|
|
}
|