From 1db452d6bb3bf573be45b833c2a18627de14d6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20N=C3=B8rfjand=20Stengaard?= Date: Thu, 28 Sep 2023 13:53:20 +0200 Subject: [PATCH] =?UTF-8?q?Add=20version=20and=20revision=20options=20to?= =?UTF-8?q?=20rdb=20fs=20add=20command=20to=20support=20adding=20file=20sy?= =?UTF-8?q?stems=20w=C3=ADthout=20version=20string.=20Fix=20windows=20phys?= =?UTF-8?q?ical=20drive=20throwing=20error=20when=20cdrom=20drive=20is=20p?= =?UTF-8?q?resent.=20Fix=20streams=20being=20closed=20by=20discutils=20dis?= =?UTF-8?q?k.=20Fix=20file=20system=20writer=20to=20create=20directory=20b?= =?UTF-8?q?efore=20writing=20entry.=20Fix=20fast=20file=20system=20dos=200?= =?UTF-8?q?=20read=20past=20end=20of=20file=20with=20file=20size=20is=20eq?= =?UTF-8?q?ual=20to=20data=20block=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Hst.Imager.ConsoleApp/CommandHandler.cs | 5 +- .../RdbCommandFactory.cs | 14 ++- .../CommandTests/FsCommandTestBase.cs | 4 +- ...venFsCopyCommandWithMbrFatFormattedDisk.cs | 105 ++++++++++++++++- ...enFsCopyCommandWithMbrNtfsFormattedDisk.cs | 4 +- .../CommandTests/GivenRdbFsAddCommand.cs | 110 ++++++++++++++++++ src/Hst.Imager.Core.Tests/GivenReadCommand.cs | 2 +- src/Hst.Imager.Core.Tests/TestMedia.cs | 1 + .../Commands/FileSystemEntryWriter.cs | 18 ++- src/Hst.Imager.Core/Commands/FsDirCommand.cs | 10 +- .../Commands/GptCommands/GptInitCommand.cs | 4 +- .../Commands/GptCommands/GptPartAddCommand.cs | 4 +- .../Commands/GptCommands/GptPartDelCommand.cs | 2 +- .../GptCommands/GptPartFormatCommand.cs | 2 +- .../Commands/MbrInitCommand.cs | 4 +- .../Commands/MbrPartAddCommand.cs | 4 +- .../Commands/MbrPartDelCommand.cs | 4 +- .../Commands/MbrPartFormatCommand.cs | 4 +- .../Commands/RdbFsAddCommand.cs | 41 +++++-- .../Commands/VersionNotFoundError.cs | 10 ++ src/Hst.Imager.Core/Hst.Imager.Core.csproj | 4 +- src/Hst.Imager.Core/MbrTest.cs | 82 ------------- .../WindowsPhysicalDriveManager.cs | 38 +++--- 23 files changed, 339 insertions(+), 137 deletions(-) create mode 100644 src/Hst.Imager.Core.Tests/CommandTests/GivenRdbFsAddCommand.cs create mode 100644 src/Hst.Imager.Core/Commands/VersionNotFoundError.cs delete mode 100644 src/Hst.Imager.Core/MbrTest.cs diff --git a/src/Hst.Imager.ConsoleApp/CommandHandler.cs b/src/Hst.Imager.ConsoleApp/CommandHandler.cs index 30f60af..fa0c7e1 100644 --- a/src/Hst.Imager.ConsoleApp/CommandHandler.cs +++ b/src/Hst.Imager.ConsoleApp/CommandHandler.cs @@ -364,10 +364,11 @@ await Execute(new RdbRestoreCommand(GetLogger(), GetCommandHe await GetPhysicalDrives(), backupPath, path)); } - public static async Task RdbFsAdd(string path, string fileSystemPath, string dosType, string fileSystemName) + public static async Task RdbFsAdd(string path, string fileSystemPath, string dosType, string fileSystemName, + int? version, int? revision) { await Execute(new RdbFsAddCommand(GetLogger(), GetCommandHelper(), - await GetPhysicalDrives(), path, fileSystemPath, dosType, fileSystemName)); + await GetPhysicalDrives(), path, fileSystemPath, dosType, fileSystemName, version, revision)); } public static async Task RdbFsDel(string path, int fileSystemNumber) diff --git a/src/Hst.Imager.ConsoleApp/RdbCommandFactory.cs b/src/Hst.Imager.ConsoleApp/RdbCommandFactory.cs index 6dc3f9b..ecce232 100644 --- a/src/Hst.Imager.ConsoleApp/RdbCommandFactory.cs +++ b/src/Hst.Imager.ConsoleApp/RdbCommandFactory.cs @@ -104,13 +104,23 @@ private static Command CreateRdbFsAdd() new[] { "--name", "-n" }, description: "Name of file system."); + var versionOption = new Option( + new[] { "--version", "-v" }, + description: "Version of file system (number before . in version)."); + + var revisionOption = new Option( + new[] { "--revision", "-r" }, + description: "Revision of file system (number after . in version)."); + var rdbFsAddCommand = new Command("add", "Add file system."); - rdbFsAddCommand.SetHandler(CommandHandler.RdbFsAdd, pathArgument, fileSystemPathArgument, dosTypeArgument, - fileSystemNameOption); + rdbFsAddCommand.SetHandler(CommandHandler.RdbFsAdd, pathArgument, fileSystemPathArgument, + dosTypeArgument, fileSystemNameOption, versionOption, revisionOption); rdbFsAddCommand.AddArgument(pathArgument); rdbFsAddCommand.AddArgument(fileSystemPathArgument); rdbFsAddCommand.AddArgument(dosTypeArgument); rdbFsAddCommand.AddOption(fileSystemNameOption); + rdbFsAddCommand.AddOption(versionOption); + rdbFsAddCommand.AddOption(revisionOption); return rdbFsAddCommand; } diff --git a/src/Hst.Imager.Core.Tests/CommandTests/FsCommandTestBase.cs b/src/Hst.Imager.Core.Tests/CommandTests/FsCommandTestBase.cs index 40a744c..09e48cc 100644 --- a/src/Hst.Imager.Core.Tests/CommandTests/FsCommandTestBase.cs +++ b/src/Hst.Imager.Core.Tests/CommandTests/FsCommandTestBase.cs @@ -42,7 +42,7 @@ protected async Task CreateMbrDisk(TestCommandHelper testCommandHelper, string p var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new DiscUtils.Raw.Disk(media.Stream, Ownership.Dispose); + : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); BiosPartitionTable.Initialize(disk); } @@ -60,7 +60,7 @@ protected async Task CreateGptDisk(TestCommandHelper testCommandHelper, string p var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new DiscUtils.Raw.Disk(media.Stream, Ownership.Dispose); + : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); GuidPartitionTable.Initialize(disk.Content, Geometry.FromCapacity(disk.Capacity)); } diff --git a/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrFatFormattedDisk.cs b/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrFatFormattedDisk.cs index 4ed256d..6998568 100644 --- a/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrFatFormattedDisk.cs +++ b/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrFatFormattedDisk.cs @@ -53,19 +53,23 @@ public async Task WhenCopyingAllRecursivelyFromDiskToLocalDirectoryThenDirectori // assert - file1.txt file was extracted var file1 = Path.Combine(destPath, "file1.txt"); - Assert.Equal(file1, files.FirstOrDefault(x => x.Equals(file1, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + Assert.Equal(file1, + files.FirstOrDefault(x => x.Equals(file1, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); // assert - file2.txt file was extracted var file2 = Path.Combine(destPath, "file2.txt"); - Assert.Equal(file2, files.FirstOrDefault(x => x.Equals(file2, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + Assert.Equal(file2, + files.FirstOrDefault(x => x.Equals(file2, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); // assert - file3.txt file was extracted var file3 = Path.Combine(destPath, "dir1", "file3.txt"); - Assert.Equal(file3, files.FirstOrDefault(x => x.Equals(file3, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + Assert.Equal(file3, + files.FirstOrDefault(x => x.Equals(file3, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); // assert - test.txt file was extracted var test = Path.Combine(destPath, "dir1", "test.txt"); - Assert.Equal(test, files.FirstOrDefault(x => x.Equals(test, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + Assert.Equal(test, + files.FirstOrDefault(x => x.Equals(test, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); } finally { @@ -83,8 +87,8 @@ private async Task CreateDirectoriesAndFiles(TestCommandHelper testCommandHelper using var media = mediaResult.Value; var stream = media.Stream; - - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(stream, Ownership.None); + + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(stream, Ownership.None); var biosPartitionTable = new BiosPartitionTable(disk); var partition = biosPartitionTable.Partitions.FirstOrDefault(); @@ -117,4 +121,93 @@ private async Task CreateDirectoriesAndFiles(TestCommandHelper testCommandHelper { } } + + [Fact] + public async Task When_CopyFromLocalSubDirectoryToDiskSubDirectory_Then_DirectoryIsCreatedAndFilesCopied() + { + var srcPath = $"{Guid.NewGuid()}"; + var destPath = $"{Guid.NewGuid()}.img"; + + try + { + // arrange - test command helper + var testCommandHelper = new TestCommandHelper(); + + // arrange - source local directory and files + var dir1Path = Path.Combine(srcPath, "src"); + Directory.CreateDirectory(dir1Path); + var file1Path = Path.Combine(dir1Path, "file1.bin"); + await File.WriteAllBytesAsync(file1Path, new byte[50000]); + var file2Path = Path.Combine(dir1Path, "file2.bin"); + await File.WriteAllBytesAsync(file2Path, new byte[250000]); + + // arrange - destination mbr fat formatted disk + testCommandHelper.AddTestMedia(destPath, 10.MB()); + await CreateMbrFatFormattedDisk(testCommandHelper, destPath, 4.GB()); + + // arrange - create fs copy command + var cancellationTokenSource = new CancellationTokenSource(); + var fsCopyCommand = new FsCopyCommand(new NullLogger(), testCommandHelper, + new List(), + Path.Combine(srcPath, "src"), Path.Combine(destPath, "mbr", "1", "dest"), true, false, true); + + // act - copy directories and files + var result = await fsCopyCommand.Execute(cancellationTokenSource.Token); + Assert.Equal(string.Empty, result.Error?.ToString() ?? string.Empty); + Assert.True(result.IsSuccess); + + // arrange - get fat file system + var mediaResult = await testCommandHelper.GetReadableMedia( + Enumerable.Empty(), destPath); + using var media = mediaResult.Value; + media.Stream.Position = 0; + var disk = media is DiskMedia diskMedia + ? diskMedia.Disk + : new DiscUtils.Raw.Disk(media.Stream, + Ownership.None); + var biosPartitionTable = new BiosPartitionTable(disk); + var partitionStream = biosPartitionTable.Partitions[0].Open(); + var fatFileSystem = new FatFileSystem(partitionStream); + + // assert - 1 directory in root directory + var dirs = fatFileSystem.GetDirectories("").ToList(); + Assert.Single(dirs); + + // assert - "dest" exists in root directory + var destDirPath = "dest"; + Assert.Equal(destDirPath, + dirs.FirstOrDefault(x => x.Equals(destDirPath, StringComparison.OrdinalIgnoreCase)) + ?.ToLowerInvariant()); + + // assert - 0 files in root directory + var files = fatFileSystem.GetFiles("").ToList(); + Assert.Empty(files); + + // assert - 0 directories in "dest" directory + dirs = fatFileSystem.GetDirectories(destDirPath).ToList(); + Assert.Empty(dirs); + + // assert - 2 files in root directory + files = fatFileSystem.GetFiles(destDirPath).ToList(); + Assert.Equal(2, files.Count); + + // assert - file1.bin file was copied and has size 50000 bytes + file1Path = Path.Combine(destDirPath, "file1.bin"); + Assert.Equal(file1Path, + files.FirstOrDefault(x => x.Equals(file1Path, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + var file1Info = fatFileSystem.GetFileInfo(file1Path); + Assert.Equal(50000, file1Info.Length); + + // assert - file2.bin file was copied and has size 250000 bytes + file2Path = Path.Combine(destDirPath, "file2.bin"); + Assert.Equal(file2Path, + files.FirstOrDefault(x => x.Equals(file2Path, StringComparison.OrdinalIgnoreCase))?.ToLowerInvariant()); + var file2Info = fatFileSystem.GetFileInfo(file2Path); + Assert.Equal(250000, file2Info.Length); + } + finally + { + DeletePaths(srcPath, destPath); + } + } } \ No newline at end of file diff --git a/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrNtfsFormattedDisk.cs b/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrNtfsFormattedDisk.cs index 46b2d8b..4e61174 100644 --- a/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrNtfsFormattedDisk.cs +++ b/src/Hst.Imager.Core.Tests/CommandTests/GivenFsCopyCommandWithMbrNtfsFormattedDisk.cs @@ -84,7 +84,9 @@ private async Task CreateMbrNtfsDirectoriesAndFiles(TestCommandHelper testComman using var media = mediaResult.Value; var stream = media.Stream; - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(stream, Ownership.None); + var disk = media is DiskMedia diskMedia + ? diskMedia.Disk + : new DiscUtils.Raw.Disk(stream, Ownership.None); var biosPartitionTable = new BiosPartitionTable(disk); var partition = biosPartitionTable.Partitions.FirstOrDefault(); diff --git a/src/Hst.Imager.Core.Tests/CommandTests/GivenRdbFsAddCommand.cs b/src/Hst.Imager.Core.Tests/CommandTests/GivenRdbFsAddCommand.cs new file mode 100644 index 0000000..54a5da5 --- /dev/null +++ b/src/Hst.Imager.Core.Tests/CommandTests/GivenRdbFsAddCommand.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Hst.Amiga.RigidDiskBlocks; +using Hst.Core.Extensions; +using Hst.Imager.Core.Commands; +using Hst.Imager.Core.Models; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; + +namespace Hst.Imager.Core.Tests.CommandTests; + +public class GivenRdbFsAddCommand : FsCommandTestBase +{ + [Fact] + public async Task When_FileSystemDoesHaveVersionStringAndVersionAndRevisionIsSet_Then_FileSystemIsAdded() + { + // arrange - path, size and test command helper + var imgPath = $"{Guid.NewGuid()}.img"; + var testCommandHelper = new TestCommandHelper(); + var diskSize = 100.MB(); + var fileSystemPath = "FastFileSystem"; + + // arrange - create rdb disk + testCommandHelper.AddTestMedia(imgPath, diskSize); + await CreateRdbDisk(testCommandHelper, imgPath, diskSize); + + // arrange - file system without version string + await testCommandHelper.AddTestMedia(fileSystemPath, fileSystemPath, data: new byte[36]); + + // arrange - rdb file system add command + var cancellationTokenSource = new CancellationTokenSource(); + var command = new RdbFsAddCommand(new NullLogger(), testCommandHelper, + new List(), imgPath, "FastFileSystem", "DOS3", + "FastFileSystem", 1, 0); + + // act - execute rdb file system add command + var result = await command.Execute(cancellationTokenSource.Token); + Assert.True(result.IsSuccess); + } + + [Fact] + public async Task When_FileSystemDoesHaveVersionStringAndVersionIsNotSet_Then_ErrorIsReturned() + { + // arrange - path, size and test command helper + var imgPath = $"{Guid.NewGuid()}.img"; + var testCommandHelper = new TestCommandHelper(); + var diskSize = 100.MB(); + var fileSystemPath = "FastFileSystem"; + + // arrange - create rdb disk + testCommandHelper.AddTestMedia(imgPath, diskSize); + await CreateRdbDisk(testCommandHelper, imgPath, diskSize); + + // arrange - file system without version string + await testCommandHelper.AddTestMedia(fileSystemPath, fileSystemPath, data: new byte[36]); + + // arrange - rdb file system add command + var cancellationTokenSource = new CancellationTokenSource(); + var command = new RdbFsAddCommand(new NullLogger(), testCommandHelper, + new List(), imgPath, "FastFileSystem", "DOS3", + "FastFileSystem", null, null); + + // act - execute rdb file system add command + var result = await command.Execute(cancellationTokenSource.Token); + Assert.False(result.IsSuccess); + Assert.True(result.IsFaulted); + Assert.IsType(result.Error); + } + + [Fact] + public async Task When_FileSystemDoesHaveVersionStringAndRevisionIsNotSet_Then_ErrorIsReturned() + { + // arrange - path, size and test command helper + var imgPath = $"{Guid.NewGuid()}.img"; + var testCommandHelper = new TestCommandHelper(); + var diskSize = 100.MB(); + var fileSystemPath = "FastFileSystem"; + + // arrange - create rdb disk + testCommandHelper.AddTestMedia(imgPath, diskSize); + await CreateRdbDisk(testCommandHelper, imgPath, diskSize); + + // arrange - file system without version string + await testCommandHelper.AddTestMedia(fileSystemPath, fileSystemPath, data: new byte[36]); + + // arrange - rdb file system add command + var cancellationTokenSource = new CancellationTokenSource(); + var command = new RdbFsAddCommand(new NullLogger(), testCommandHelper, + new List(), imgPath, "FastFileSystem", "DOS3", + "FastFileSystem", 1, null); + + // act - execute rdb file system add command + var result = await command.Execute(cancellationTokenSource.Token); + Assert.False(result.IsSuccess); + Assert.True(result.IsFaulted); + Assert.IsType(result.Error); + } + + private static async Task CreateRdbDisk(TestCommandHelper testCommandHelper, string path, long diskSize) + { + var mediaResult = await testCommandHelper.GetWritableFileMedia(path, diskSize, true); + using var media = mediaResult.Value; + var stream = media is DiskMedia diskMedia ? diskMedia.Disk.Content : media.Stream; + + var rigidDiskBlock = RigidDiskBlock.Create(diskSize.ToSectorSize()); + await RigidDiskBlockWriter.WriteBlock(rigidDiskBlock, stream); + } +} \ No newline at end of file diff --git a/src/Hst.Imager.Core.Tests/GivenReadCommand.cs b/src/Hst.Imager.Core.Tests/GivenReadCommand.cs index 633fbb9..3ec76c3 100644 --- a/src/Hst.Imager.Core.Tests/GivenReadCommand.cs +++ b/src/Hst.Imager.Core.Tests/GivenReadCommand.cs @@ -316,7 +316,7 @@ public async Task WhenReadPhysicalDriveToVhdWithSizeNotDividableBy512ThenReadDat // assert - source bytes are equal to destination bytes destinationBytes = destinationBytes.Take(size).ToArray(); Assert.Equal(sourceBytes.Length, destinationBytes.Length); - Assert.Equal(sourceBytes, destinationBytes); + Assert.True(sourceBytes.SequenceEqual(destinationBytes)); } finally { diff --git a/src/Hst.Imager.Core.Tests/TestMedia.cs b/src/Hst.Imager.Core.Tests/TestMedia.cs index 81a2a7f..b2cac0e 100644 --- a/src/Hst.Imager.Core.Tests/TestMedia.cs +++ b/src/Hst.Imager.Core.Tests/TestMedia.cs @@ -26,6 +26,7 @@ public async Task WriteData(byte[] data) { Stream.Seek(0, SeekOrigin.Begin); await Stream.WriteBytes(data); + Stream.Seek(0, SeekOrigin.Begin); } public async Task ReadData() diff --git a/src/Hst.Imager.Core/Commands/FileSystemEntryWriter.cs b/src/Hst.Imager.Core/Commands/FileSystemEntryWriter.cs index 02ef978..8cb53cc 100644 --- a/src/Hst.Imager.Core/Commands/FileSystemEntryWriter.cs +++ b/src/Hst.Imager.Core/Commands/FileSystemEntryWriter.cs @@ -15,6 +15,7 @@ public class FileSystemEntryWriter : IEntryWriter private readonly Media media; private readonly string[] pathComponents; private readonly IFileSystem fileSystem; + private string[] currentPathComponents; private bool disposed; public FileSystemEntryWriter(Media media, IFileSystem fileSystem, string[] pathComponents) @@ -23,6 +24,7 @@ public FileSystemEntryWriter(Media media, IFileSystem fileSystem, string[] pathC this.media = media; this.pathComponents = pathComponents; this.fileSystem = fileSystem; + this.currentPathComponents = Array.Empty(); } public string MediaPath => this.media.Path; @@ -49,7 +51,7 @@ private void Dispose(bool disposing) } public void Dispose() => Dispose(true); - + public Task CreateDirectory(Entry entry, string[] entryPathComponents, bool skipAttributes) { var fullPathComponents = pathComponents.Concat(entryPathComponents).ToArray(); @@ -59,12 +61,26 @@ public Task CreateDirectory(Entry entry, string[] entryPathComponents, bool skip fileSystem.CreateDirectory(string.Join("\\", fullPathComponents.Take(i))); } + currentPathComponents = fullPathComponents; + return Task.CompletedTask; } public async Task WriteEntry(Entry entry, string[] entryPathComponents, Stream stream, bool skipAttributes) { var fullPathComponents = pathComponents.Concat(entryPathComponents).ToArray(); + + var directoryChanged = currentPathComponents.Length != fullPathComponents.Length - 1; + if (directoryChanged && fullPathComponents.Length > 1) + { + for (var i = 1; i < fullPathComponents.Length; i++) + { + fileSystem.CreateDirectory(string.Join("\\", fullPathComponents.Take(i))); + } + + currentPathComponents = fullPathComponents.Take(fullPathComponents.Length - 1).ToArray(); + } + var fullPath = string.Join("\\", fullPathComponents); await using var entryStream = fileSystem.OpenFile(fullPath, FileMode.OpenOrCreate); diff --git a/src/Hst.Imager.Core/Commands/FsDirCommand.cs b/src/Hst.Imager.Core/Commands/FsDirCommand.cs index a94d24a..3847227 100644 --- a/src/Hst.Imager.Core/Commands/FsDirCommand.cs +++ b/src/Hst.Imager.Core/Commands/FsDirCommand.cs @@ -208,7 +208,9 @@ private Task ListMbrPartitions(Media media) { OnDebugMessage("Reading Master Boot Record"); - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); + var disk = media is DiskMedia diskMedia + ? diskMedia.Disk + : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); BiosPartitionTable biosPartitionTable; try @@ -250,7 +252,7 @@ private Task ListMbrPartitions(Media media) private async Task ListMbrPartitionEntries(Media media, string[] parts) { - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); var mbrFileSystemResult = await MountMbrFileSystem(disk, parts[0]); if (mbrFileSystemResult.IsFaulted) @@ -268,7 +270,7 @@ private async Task ListMbrPartitionEntries(Media media, string[] parts) private async Task ListGptPartitionEntries(Media media, string[] parts) { - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); var gptFileSystemResult = await MountGptFileSystem(disk, parts[0]); if (gptFileSystemResult.IsFaulted) @@ -288,7 +290,7 @@ private Task ListGptPartitions(Media media) { OnDebugMessage("Reading Guid Partition Table"); - using var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new DiscUtils.Raw.Disk(media.Stream, Ownership.None); GuidPartitionTable guidPartitionTable; try diff --git a/src/Hst.Imager.Core/Commands/GptCommands/GptInitCommand.cs b/src/Hst.Imager.Core/Commands/GptCommands/GptInitCommand.cs index 7b15ae1..c9bd672 100644 --- a/src/Hst.Imager.Core/Commands/GptCommands/GptInitCommand.cs +++ b/src/Hst.Imager.Core/Commands/GptCommands/GptInitCommand.cs @@ -43,9 +43,9 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); var deleteFirstSectors = false; diff --git a/src/Hst.Imager.Core/Commands/GptCommands/GptPartAddCommand.cs b/src/Hst.Imager.Core/Commands/GptCommands/GptPartAddCommand.cs index 9fb3d6e..92c80a6 100644 --- a/src/Hst.Imager.Core/Commands/GptCommands/GptPartAddCommand.cs +++ b/src/Hst.Imager.Core/Commands/GptCommands/GptPartAddCommand.cs @@ -76,9 +76,9 @@ public override async Task Execute(CancellationToken token) OnDebugMessage("Reading Guid Partition Table"); - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); GuidPartitionTable guidPartitionTable; try diff --git a/src/Hst.Imager.Core/Commands/GptCommands/GptPartDelCommand.cs b/src/Hst.Imager.Core/Commands/GptCommands/GptPartDelCommand.cs index fad7b0c..96c4ac3 100644 --- a/src/Hst.Imager.Core/Commands/GptCommands/GptPartDelCommand.cs +++ b/src/Hst.Imager.Core/Commands/GptCommands/GptPartDelCommand.cs @@ -45,7 +45,7 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new Disk(media.Stream, Ownership.None); diff --git a/src/Hst.Imager.Core/Commands/GptCommands/GptPartFormatCommand.cs b/src/Hst.Imager.Core/Commands/GptCommands/GptPartFormatCommand.cs index 4251d99..89aeb43 100644 --- a/src/Hst.Imager.Core/Commands/GptCommands/GptPartFormatCommand.cs +++ b/src/Hst.Imager.Core/Commands/GptCommands/GptPartFormatCommand.cs @@ -51,7 +51,7 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk : new Disk(media.Stream, Ownership.None); diff --git a/src/Hst.Imager.Core/Commands/MbrInitCommand.cs b/src/Hst.Imager.Core/Commands/MbrInitCommand.cs index a1087c3..5b03c76 100644 --- a/src/Hst.Imager.Core/Commands/MbrInitCommand.cs +++ b/src/Hst.Imager.Core/Commands/MbrInitCommand.cs @@ -43,9 +43,9 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); var deleteSectorCount = 0L; diff --git a/src/Hst.Imager.Core/Commands/MbrPartAddCommand.cs b/src/Hst.Imager.Core/Commands/MbrPartAddCommand.cs index f514195..93557bd 100644 --- a/src/Hst.Imager.Core/Commands/MbrPartAddCommand.cs +++ b/src/Hst.Imager.Core/Commands/MbrPartAddCommand.cs @@ -75,9 +75,9 @@ public override async Task Execute(CancellationToken token) OnDebugMessage("Reading Master Boot Record"); - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); BiosPartitionTable biosPartitionTable; try diff --git a/src/Hst.Imager.Core/Commands/MbrPartDelCommand.cs b/src/Hst.Imager.Core/Commands/MbrPartDelCommand.cs index 41eb92b..70249c9 100644 --- a/src/Hst.Imager.Core/Commands/MbrPartDelCommand.cs +++ b/src/Hst.Imager.Core/Commands/MbrPartDelCommand.cs @@ -45,9 +45,9 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); OnDebugMessage("Reading Master Boot Record"); diff --git a/src/Hst.Imager.Core/Commands/MbrPartFormatCommand.cs b/src/Hst.Imager.Core/Commands/MbrPartFormatCommand.cs index bb59082..758ec45 100644 --- a/src/Hst.Imager.Core/Commands/MbrPartFormatCommand.cs +++ b/src/Hst.Imager.Core/Commands/MbrPartFormatCommand.cs @@ -50,9 +50,9 @@ public override async Task Execute(CancellationToken token) } using var media = mediaResult.Value; - using var disk = media is DiskMedia diskMedia + var disk = media is DiskMedia diskMedia ? diskMedia.Disk - : new Disk(media.Stream, Ownership.Dispose); + : new Disk(media.Stream, Ownership.None); OnDebugMessage("Reading Master Boot Record"); diff --git a/src/Hst.Imager.Core/Commands/RdbFsAddCommand.cs b/src/Hst.Imager.Core/Commands/RdbFsAddCommand.cs index 7271698..0eb79dd 100644 --- a/src/Hst.Imager.Core/Commands/RdbFsAddCommand.cs +++ b/src/Hst.Imager.Core/Commands/RdbFsAddCommand.cs @@ -22,16 +22,21 @@ public class RdbFsAddCommand : CommandBase private readonly string path; private readonly string dosType; private readonly string fileSystemPath; + private readonly int? version; + private readonly int? revision; private readonly string fileSystemName; public RdbFsAddCommand(ILogger logger, ICommandHelper commandHelper, - IEnumerable physicalDrives, string path, string fileSystemPath, string dosType, string fileSystemName) + IEnumerable physicalDrives, string path, string fileSystemPath, string dosType, + string fileSystemName, int? version, int? revision) { this.logger = logger; this.commandHelper = commandHelper; this.physicalDrives = physicalDrives; this.path = path; this.fileSystemPath = fileSystemPath; + this.version = version; + this.revision = revision; this.dosType = string.IsNullOrWhiteSpace(dosType) ? "DOS3" : dosType.ToUpper(); this.fileSystemName = string.IsNullOrWhiteSpace(fileSystemName) ? "FastFileSystem" : fileSystemName; } @@ -67,7 +72,14 @@ public override async Task Execute(CancellationToken token) OnDebugMessage($"Opening path '{fileSystemPath}' for reading file system"); - await using var fileSystemStream = File.OpenRead(fileSystemPath); + var fileSystemMediaResult = await commandHelper.GetReadableFileMedia(fileSystemPath); + if (fileSystemMediaResult.IsFaulted) + { + return new Result(fileSystemMediaResult.Error); + } + + using var fileSystemMedia = fileSystemMediaResult.Value; + var fileSystemStream = fileSystemMedia.Stream; OnDebugMessage("Read file system from file"); @@ -79,16 +91,31 @@ public override async Task Execute(CancellationToken token) var fileSystemBytes = await fileSystemStream.ReadBytes((int)fileSystemStream.Length); - var version = await VersionStringReader.Read(new MemoryStream(fileSystemBytes)); - if (string.IsNullOrWhiteSpace(version)) + var versionString = await VersionStringReader.Read(new MemoryStream(fileSystemBytes)); + AmigaVersion fileVersion = null; + if (!string.IsNullOrWhiteSpace(versionString)) { - return new Result(new Error("Version string is empty")); + fileVersion = VersionStringReader.Parse(versionString); } - var fileVersion = VersionStringReader.Parse(version); if (fileVersion == null) { - return new Result(new Error($"Parse version failed")); + if (!version.HasValue) + { + return new Result(new VersionNotFoundError($"Version string not found in file system file. Required version must be set manually")); + } + + if (!revision.HasValue) + { + return new Result(new VersionNotFoundError($"Version string not found in file system file. Required revision must be set manually")); + } + + fileVersion = new AmigaVersion + { + Name = Path.GetFileName(fileSystemPath), + Revision = revision.Value, + Version = version.Value + }; } var fileSystemHeaderBlock = BlockHelper.CreateFileSystemHeaderBlock(DosTypeHelper.FormatDosType(dosType), diff --git a/src/Hst.Imager.Core/Commands/VersionNotFoundError.cs b/src/Hst.Imager.Core/Commands/VersionNotFoundError.cs new file mode 100644 index 0000000..3266752 --- /dev/null +++ b/src/Hst.Imager.Core/Commands/VersionNotFoundError.cs @@ -0,0 +1,10 @@ +using Hst.Core; + +namespace Hst.Imager.Core.Commands; + +public class VersionNotFoundError : Error +{ + public VersionNotFoundError(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/src/Hst.Imager.Core/Hst.Imager.Core.csproj b/src/Hst.Imager.Core/Hst.Imager.Core.csproj index 95ac0ca..73fb232 100644 --- a/src/Hst.Imager.Core/Hst.Imager.Core.csproj +++ b/src/Hst.Imager.Core/Hst.Imager.Core.csproj @@ -2,8 +2,8 @@ - - + + diff --git a/src/Hst.Imager.Core/MbrTest.cs b/src/Hst.Imager.Core/MbrTest.cs deleted file mode 100644 index ed8869c..0000000 --- a/src/Hst.Imager.Core/MbrTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Hst.Imager.Core -{ - using System; - using System.IO; - using System.Text; - using DiscUtils; - using DiscUtils.Fat; - using DiscUtils.Partitions; - using DiscUtils.Raw; - using DiscUtils.Streams; - //using DiscUtils.Vhd; - - public class MbrTest - { - public void Create() - { - var size = 1024 * 1024 * 1024; // 1gb - - - var sectorSize = 512; - var headsPerCylinder = 16; - var sectorsPerTrack = 63; - var cylinders = size / (headsPerCylinder * sectorsPerTrack * sectorSize); - - var path = @"d:\temp\mbr.img"; - - using var imgStream = File.Create(path); - //var disk = Disk.Initialize(imgStream, Ownership.None, size, new Geometry(cylinders, headsPerCylinder, sectorsPerTrack, sectorSize)); - var disk = Disk.Initialize(imgStream, Ownership.None, size); - var biosPartitionTable = BiosPartitionTable.Initialize(disk); - // biosPartitionTable.CreatePrimaryBySector() - - // var bootSector = new byte[512]; - // imgStream.Position = 0; - // imgStream.Read(bootSector, 0, bootSector.Length); - // EndianUtilities.WriteBytesLittleEndian((ushort)sectorSize, bootSector, 11); - // //EndianUtilities.ToUInt16LittleEndian(bpb, 11); - // imgStream.Position = 0; - // imgStream.Write(bootSector, 0, bootSector.Length); - - var partitionSize = 1024 * 1024 * 512; - var partitionCylinders = partitionSize / (headsPerCylinder * sectorsPerTrack * sectorSize); - var partitionSectors = partitionSize / sectorSize; - - // using FatFileSystem fs = FatFileSystem.FormatPartition(imgStream, "AmigaTest", - // new Geometry(partitionCylinders, headsPerCylinder, sectorsPerTrack, sectorSize), 1024 * 512, - // partitionSectors, 0); - // fs.CreateDirectory(@"TestDir\CHILD"); - using FatFileSystem fs = FatFileSystem.FormatPartition(disk, 0, "AmigaMBRTest"); - - var bootSector = new byte[512]; - imgStream.Position = 0; - imgStream.Read(bootSector, 0, bootSector.Length); - - fs.CreateDirectory(@"TestDir\CHILD"); - - using var s = fs.OpenFile("foo.txt", FileMode.Create); - using var writer = new StreamWriter(s, Encoding.UTF8); - writer.WriteLine("hello"); - - fs.Dispose(); - disk.Dispose(); - } - - public void Read() - { - var path = @"d:\temp\mbr.img"; - - DiscUtils.Containers.SetupHelper.SetupContainers(); - DiscUtils.FileSystems.SetupHelper.SetupFileSystems(); - var disk = VirtualDisk.OpenDisk(path, FileAccess.ReadWrite); - var fs = new FatFileSystem(disk.Partitions[0].Open()); - var directories = fs.GetDirectories(""); - var files = fs.GetFiles(""); - } - - private int ConvertToSector(long offset) - { - return Convert.ToInt32(offset / 512); - } - } -} \ No newline at end of file diff --git a/src/Hst.Imager.Core/PhysicalDrives/WindowsPhysicalDriveManager.cs b/src/Hst.Imager.Core/PhysicalDrives/WindowsPhysicalDriveManager.cs index a47e80d..aa81361 100644 --- a/src/Hst.Imager.Core/PhysicalDrives/WindowsPhysicalDriveManager.cs +++ b/src/Hst.Imager.Core/PhysicalDrives/WindowsPhysicalDriveManager.cs @@ -50,26 +50,38 @@ protected virtual IEnumerable GetPhysicalDrivesUsingKernel32() var drives = DriveInfo.GetDrives().ToList(); foreach (var drive in drives) { - var driveName = drive.Name[..2]; - var drivePath = $"\\\\.\\{driveName}"; - logger.LogDebug($"Opening win32 raw disk access to drive letter '{driveName}'"); - - using var win32RawDisk = new Win32RawDisk(drivePath, false, true); - if (win32RawDisk.IsInvalid()) + if (drive.DriveType == DriveType.CDRom) { continue; } + + try + { + var driveName = drive.Name[..2]; + var drivePath = $"\\\\.\\{driveName}"; + logger.LogDebug($"Opening win32 raw disk access to drive letter '{driveName}'"); - var diskExtendsResult = win32RawDisk.DiskExtends(); - logger.LogDebug( - $"Disk extends returned disk number {diskExtendsResult.DiskNumber} for drive letter '{driveName}'"); + using var win32RawDisk = new Win32RawDisk(drivePath, false, true); + if (win32RawDisk.IsInvalid()) + { + continue; + } - if (!physicalDriveLettersIndex.ContainsKey(diskExtendsResult.DiskNumber)) + var diskExtendsResult = win32RawDisk.DiskExtends(); + logger.LogDebug( + $"Disk extends returned disk number {diskExtendsResult.DiskNumber} for drive letter '{driveName}'"); + + if (!physicalDriveLettersIndex.ContainsKey(diskExtendsResult.DiskNumber)) + { + physicalDriveLettersIndex.Add(diskExtendsResult.DiskNumber, new List()); + } + + physicalDriveLettersIndex[diskExtendsResult.DiskNumber].Add(driveName); + } + catch (Exception) { - physicalDriveLettersIndex.Add(diskExtendsResult.DiskNumber, new List()); + // ignore } - - physicalDriveLettersIndex[diskExtendsResult.DiskNumber].Add(driveName); } // iterate physical drives, get media type from geometry, get name from storage property query and size