Move DevDrive project to repo root
This commit is contained in:
213
FloppyStorage.cs
Normal file
213
FloppyStorage.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user