From 146943c24339f471bc0987a61f5929b11ed169db Mon Sep 17 00:00:00 2001
From: TheEndHunter <8478343+TheEndHunter@users.noreply.github.com>
Date: Wed, 17 Jul 2024 17:49:01 +0100
Subject: [PATCH] Support for Fixed VHDX Files, and some package Updates (#12)
* Added Experimental Support for .NET 9.0,
Implemented InitializeFixedInternal for vhdx files.
Added and turn on Tests for fixed VHDX files.
TODO: double check to make 100% sure that the test coverage covers the new added feature properly.
Tests as it stands all pass across all supported frameworks, additonal testing as required.
* Changes to test library project file to allow newer versions of packages across supported framework builds
* Removed .NET9.0 as target for build, this will be moved into it's own branch.
Changed FileParametersFlags.Fixed to FileParametersFlags.LeaveBlockAllocated and also put small comments on the invidiual flags.
All tests have been run, All pass across all frameworks(ran using 'dotnet test' command)
* fixed issue with file not allocating correctly, turns out fileEnd was still set at 1MB, this has been set to capacity.
Modified 2 tests as they try to allocate alomst 17GB of ram on a fixed disc, which then would fail.
All tests are passing, across all frameworks.
---
Library/DiscUtils.Core/DiscUtils.Core.csproj | 8 -
Library/DiscUtils.Vhdx/DiskBuilder.cs | 40 ++-
Library/DiscUtils.Vhdx/DiskImageFile.cs | 104 +++++-
Library/DiscUtils.Vhdx/DiskImageFileInfo.cs | 10 +-
Library/DiscUtils.Vhdx/DiskType.cs | 2 +-
Library/DiscUtils.Vhdx/FileParameters.cs | 1 +
Library/DiscUtils.Vhdx/FileParametersFlags.cs | 15 +-
Tests/LibraryTests/LibraryTests.csproj | 35 +-
Tests/LibraryTests/Vhd/DiskBuilderTest.cs | 39 ++-
Tests/LibraryTests/Vhd/DiskImageFileTest.cs | 2 +-
Tests/LibraryTests/Vhdx/DiskBuilderTest.cs | 59 +++-
Tests/LibraryTests/Vhdx/DiskTest.cs | 78 ++++-
Utilities/VHDXCreate/Program.cs | 2 +-
Utilities/VHDXDump/Program.cs | 2 +-
workload-install.ps1 | 304 ++++++++++++++++++
15 files changed, 620 insertions(+), 81 deletions(-)
create mode 100644 workload-install.ps1
diff --git a/Library/DiscUtils.Core/DiscUtils.Core.csproj b/Library/DiscUtils.Core/DiscUtils.Core.csproj
index 2f572d8d2..07ddb1a22 100644
--- a/Library/DiscUtils.Core/DiscUtils.Core.csproj
+++ b/Library/DiscUtils.Core/DiscUtils.Core.csproj
@@ -5,10 +5,6 @@
Kenneth Bell;Quamotion;LordMike;Olof Lagerkvist
DiscUtils;VHD;VDI;XVA;VMDK;ISO;NTFS;EXT2FS
-
-
-
-
@@ -21,9 +17,5 @@
-
-
-
-
diff --git a/Library/DiscUtils.Vhdx/DiskBuilder.cs b/Library/DiscUtils.Vhdx/DiskBuilder.cs
index 062f157a5..6fb6472ad 100644
--- a/Library/DiscUtils.Vhdx/DiskBuilder.cs
+++ b/Library/DiscUtils.Vhdx/DiskBuilder.cs
@@ -100,11 +100,6 @@ public DiskStreamBuilder(SparseStream content, DiskType diskType, long blockSize
protected override List FixExtents(out long totalLength)
{
- if (_diskType != DiskType.Dynamic)
- {
- throw new NotSupportedException("Creation of Vhdx currently only supports dynamically expanding format");
- }
-
var extents = new List();
var logicalSectorSize = 512;
@@ -169,12 +164,37 @@ protected override List FixExtents(out long totalLength)
extents.Add(ExtentForStruct(regionTable, 192 * Sizes.OneKiB));
extents.Add(ExtentForStruct(regionTable, 256 * Sizes.OneKiB));
- // Metadata
- var fileParams = new FileParameters
+ FileParameters fileParams;
+ if (_diskType == DiskType.Fixed)
{
- BlockSize = (uint)_blockSize,
- Flags = FileParametersFlags.None
- };
+ // Metadata
+ fileParams = new FileParameters
+ {
+ BlockSize = (uint)_blockSize,
+ Flags = FileParametersFlags.LeaveBlocksAllocated
+ };
+ }
+ else if(_diskType == DiskType.Differencing)
+ {
+ fileParams = new FileParameters
+ {
+ BlockSize = (uint)_blockSize,
+ Flags = FileParametersFlags.HasParent
+ };
+ }
+ else if(_diskType == DiskType.Dynamic)
+ {
+ // Metadata
+ fileParams = new FileParameters
+ {
+ BlockSize = (uint)_blockSize,
+ Flags = FileParametersFlags.None
+ };
+ }
+ else
+ {
+ throw new InvalidOperationException("Unknown disk type");
+ }
var metadataBuffer = new byte[metadataRegion.Length];
var metadataStream = new MemoryStream(metadataBuffer);
diff --git a/Library/DiscUtils.Vhdx/DiskImageFile.cs b/Library/DiscUtils.Vhdx/DiskImageFile.cs
index 6b11e723f..6c9491500 100644
--- a/Library/DiscUtils.Vhdx/DiskImageFile.cs
+++ b/Library/DiscUtils.Vhdx/DiskImageFile.cs
@@ -498,9 +498,96 @@ protected override void Dispose(bool disposing)
}
}
+ public static byte[] oneMiB = new byte[1024 * 1024];
private static void InitializeFixedInternal(Stream stream, long capacity, Geometry? geometry)
{
- throw new NotImplementedException();
+ geometry ??= Geometry.FromCapacity(capacity);
+
+ var logicalSectorSize = geometry.Value.BytesPerSector;
+ var physicalSectorSize = 4096;
+ const uint blockSize = FileParameters.DefaultFixedBlockSize;
+ var chunkRatio = 0x800000L * logicalSectorSize / blockSize;
+ var dataBlocksCount = MathUtilities.Ceil(capacity, blockSize);
+ var sectorBitmapBlocksCount = MathUtilities.Ceil(dataBlocksCount, chunkRatio);
+ var totalBatEntriesFixed = dataBlocksCount + sectorBitmapBlocksCount;
+ var fileHeader = new FileHeader { Creator = ".NET DiscUtils" };
+
+ long fileEnd = capacity;
+
+ var header1 = new VhdxHeader
+ {
+ SequenceNumber = 0,
+ FileWriteGuid = Guid.NewGuid(),
+ DataWriteGuid = Guid.NewGuid(),
+ LogGuid = Guid.Empty,
+ LogVersion = 0,
+ Version = 1,
+ LogLength = (uint)Sizes.OneMiB,
+ LogOffset = (ulong)fileEnd
+ };
+ header1.CalcChecksum();
+
+ fileEnd += header1.LogLength;
+
+ var header2 = new VhdxHeader(header1)
+ {
+ SequenceNumber = 1
+ };
+ header2.CalcChecksum();
+
+ var regionTable = new RegionTable();
+
+ var metadataRegion = new RegionEntry
+ {
+ Guid = RegionEntry.MetadataRegionGuid,
+ FileOffset = fileEnd,
+ Length = (uint)Sizes.OneMiB,
+ Flags = RegionFlags.Required
+ };
+ regionTable.Regions.Add(metadataRegion.Guid, metadataRegion);
+
+ fileEnd += metadataRegion.Length;
+
+ var batRegion = new RegionEntry
+ {
+ Guid = RegionEntry.BatGuid,
+ FileOffset = 3 * Sizes.OneMiB,
+ Length = (uint)MathUtilities.RoundUp(totalBatEntriesFixed * 8, Sizes.OneMiB),
+ Flags = RegionFlags.Required
+ };
+ regionTable.Regions.Add(batRegion.Guid, batRegion);
+
+ fileEnd += batRegion.Length;
+
+ stream.Position = 0;
+ stream.WriteStruct(fileHeader);
+
+ stream.Position = 64 * Sizes.OneKiB;
+ stream.WriteStruct(header1);
+
+ stream.Position = 128 * Sizes.OneKiB;
+ stream.WriteStruct(header2);
+
+ stream.Position = 192 * Sizes.OneKiB;
+ stream.WriteStruct(regionTable);
+
+ stream.Position = 256 * Sizes.OneKiB;
+ stream.WriteStruct(regionTable);
+
+ // Set stream to min size
+ stream.Position = fileEnd - 1;
+ stream.WriteByte(0);
+
+ // Metadata
+ var fileParams = new FileParameters
+ {
+ BlockSize = FileParameters.DefaultFixedBlockSize,
+ Flags = FileParametersFlags.LeaveBlocksAllocated,
+ };
+
+ var metadataStream = new SubStream(stream, metadataRegion.FileOffset, metadataRegion.Length);
+ _ = Metadata.Initialize(metadataStream, fileParams, (ulong)capacity,
+ (uint)logicalSectorSize, (uint)physicalSectorSize, null);
}
private static void InitializeDynamicInternal(Stream stream, long capacity, Geometry? geometry, long blockSize)
@@ -592,7 +679,7 @@ private static void InitializeDynamicInternal(Stream stream, long capacity, Geom
var fileParams = new FileParameters
{
BlockSize = (uint)blockSize,
- Flags = FileParametersFlags.None
+ Flags = FileParametersFlags.None,
};
var metadataStream = new SubStream(stream, metadataRegion.FileOffset, metadataRegion.Length);
@@ -605,7 +692,8 @@ private static void InitializeDifferencingInternal(Stream stream, DiskImageFile
{
var logicalSectorSize = parent._metadata.LogicalSectorSize;
var physicalSectorSize = parent._metadata.PhysicalSectorSize;
- var blockSize = parent._metadata.FileParameters.BlockSize;
+
+ uint blockSize = parent._metadata.FileParameters.BlockSize;
var capacity = parent._metadata.DiskSize;
var chunkRatio = 0x800000L * logicalSectorSize / blockSize;
@@ -685,7 +773,7 @@ private static void InitializeDifferencingInternal(Stream stream, DiskImageFile
var fileParams = new FileParameters
{
BlockSize = blockSize,
- Flags = FileParametersFlags.HasParent
+ Flags = FileParametersFlags.HasParent,
};
var parentLocator = new ParentLocator(parent._header.DataWriteGuid.ToString("b"), parentRelativePath, parentAbsolutePath);
@@ -714,6 +802,7 @@ private void Initialize()
ReadMetadata();
_batStream = OpenRegion(RegionTable.BatGuid);
+
_freeSpace.Reserve(BatControlledFileExtents());
// Indicate the file is open for modification
@@ -728,10 +817,9 @@ private List BatControlledFileExtents()
{
_batStream.Position = 0;
var batData = _batStream.ReadExactly((int)_batStream.Length);
-
- var blockSize = _metadata.FileParameters.BlockSize;
- var chunkSize = (1L << 23) * _metadata.LogicalSectorSize;
- var chunkRatio = (int)(chunkSize / _metadata.FileParameters.BlockSize);
+ uint blockSize = _metadata.FileParameters.BlockSize;
+ long chunkSize = (1L << 23) * _metadata.LogicalSectorSize;
+ int chunkRatio = (int)(chunkSize / blockSize);
var extents = new List();
for (var i = 0; i < batData.Length; i += 8)
diff --git a/Library/DiscUtils.Vhdx/DiskImageFileInfo.cs b/Library/DiscUtils.Vhdx/DiskImageFileInfo.cs
index 937251e5d..5a93d5d80 100644
--- a/Library/DiscUtils.Vhdx/DiskImageFileInfo.cs
+++ b/Library/DiscUtils.Vhdx/DiskImageFileInfo.cs
@@ -121,9 +121,15 @@ public IEnumerable ActiveLogSequence
public bool HasParent => (_metadata.FileParameters.Flags & FileParametersFlags.HasParent) != 0;
///
- /// Gets a value indicating whether blocks should be left allocated within the file.
+ /// Gets a value indicaticating if (the VHDX file is a fixed disk.
///
- public bool LeaveBlocksAllocated => (_metadata.FileParameters.Flags & FileParametersFlags.LeaveBlocksAllocated) != 0;
+ public bool IsFixedDisk => (_metadata.FileParameters.Flags & FileParametersFlags.LeaveBlocksAllocated) != 0;
+
+ ///
+ /// Gets a value indicaticating if the VHDX file is a dynamic disk.
+ ///
+ public bool IsDynamicDisk => (_metadata.FileParameters.Flags & FileParametersFlags.LeaveBlocksAllocated) == 0;
+
///
/// Gets the logical sector size of the disk represented by the VHDX file.
diff --git a/Library/DiscUtils.Vhdx/DiskType.cs b/Library/DiscUtils.Vhdx/DiskType.cs
index aa14ef15a..5f870d193 100644
--- a/Library/DiscUtils.Vhdx/DiskType.cs
+++ b/Library/DiscUtils.Vhdx/DiskType.cs
@@ -33,7 +33,7 @@ public enum DiskType
None = 0,
///
- /// Fixed-size disk, with space allocated up-front.
+ /// LeaveBlocksAllocated-size disk, with space allocated up-front.
///
Fixed = 2,
diff --git a/Library/DiscUtils.Vhdx/FileParameters.cs b/Library/DiscUtils.Vhdx/FileParameters.cs
index 76a88ccc7..6a694eae8 100644
--- a/Library/DiscUtils.Vhdx/FileParameters.cs
+++ b/Library/DiscUtils.Vhdx/FileParameters.cs
@@ -27,6 +27,7 @@ namespace DiscUtils.Vhdx;
internal sealed class FileParameters : IByteArraySerializable
{
+ public const uint DefaultFixedBlockSize = (uint)Sizes.OneMiB;
public const uint DefaultBlockSize = 32 * (uint)Sizes.OneMiB;
public const uint DefaultDifferencingBlockSize = 2 * (uint)Sizes.OneMiB;
public const uint DefaultDynamicBlockSize = 32 * (uint)Sizes.OneMiB;
diff --git a/Library/DiscUtils.Vhdx/FileParametersFlags.cs b/Library/DiscUtils.Vhdx/FileParametersFlags.cs
index 62f71145e..ec50fdb2a 100644
--- a/Library/DiscUtils.Vhdx/FileParametersFlags.cs
+++ b/Library/DiscUtils.Vhdx/FileParametersFlags.cs
@@ -27,7 +27,16 @@ namespace DiscUtils.Vhdx;
[Flags]
internal enum FileParametersFlags : uint
{
- None = 0x00,
- LeaveBlocksAllocated = 0x01,
- HasParent = 0x02
+ ///
+ /// If no bits are set, the disk will be considered Dynamic.
+ ///
+ None = 0,
+ ///
+ /// If set tells system to leave blocks allocated(fixed disk), otherwise it is a dynamic/differencing disk.
+ ///
+ LeaveBlocksAllocated = 1, //if bit 0 is set, the file is a fixed VHD, otherwise it is a dynamic/differencing VHDX.
+ ///
+ /// If this bit is set the file has a parent file, so it's most likely a differencing disk.
+ ///
+ HasParent = 2,
}
\ No newline at end of file
diff --git a/Tests/LibraryTests/LibraryTests.csproj b/Tests/LibraryTests/LibraryTests.csproj
index b754a2783..101ac1d3e 100644
--- a/Tests/LibraryTests/LibraryTests.csproj
+++ b/Tests/LibraryTests/LibraryTests.csproj
@@ -8,6 +8,8 @@
false
Latest
../../SigningKey.snk
+ true
+ true
@@ -18,17 +20,30 @@
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
-
+
+
diff --git a/Tests/LibraryTests/Vhd/DiskBuilderTest.cs b/Tests/LibraryTests/Vhd/DiskBuilderTest.cs
index bbc090673..4606149ba 100644
--- a/Tests/LibraryTests/Vhd/DiskBuilderTest.cs
+++ b/Tests/LibraryTests/Vhd/DiskBuilderTest.cs
@@ -30,22 +30,39 @@ namespace LibraryTests.Vhd;
public class DiskBuilderTest
{
- private SparseStream diskContent;
+ private SparseStream dynamic_diskContent;
+ private SparseStream fixed_diskContent;
public DiskBuilderTest()
{
- var fileStream = new MemoryStream();
- var baseFile = Disk.InitializeDynamic(fileStream, Ownership.Dispose, 16 * 1024L * 1024);
- for (var i = 0; i < 8; i += 1024 * 1024)
{
- baseFile.Content.Position = i;
- baseFile.Content.WriteByte((byte)i);
+ var fileStream = new MemoryStream();
+ var baseFile = Disk.InitializeDynamic(fileStream, Ownership.Dispose, 16 * 1024L * 1024);
+ for (var i = 0; i < 8; i += 1024 * 1024)
+ {
+ baseFile.Content.Position = i;
+ baseFile.Content.WriteByte((byte)i);
+ }
+
+ baseFile.Content.Position = 15 * 1024 * 1024;
+ baseFile.Content.WriteByte(0xFF);
+
+ dynamic_diskContent = baseFile.Content;
}
+ {
+ var fileStream = new MemoryStream();
+ var baseFile = Disk.InitializeFixed(fileStream, Ownership.Dispose, 16 * 1024L * 1024);
+ for (var i = 0; i < 8; i += 1024 * 1024)
+ {
+ baseFile.Content.Position = i;
+ baseFile.Content.WriteByte((byte)i);
+ }
- baseFile.Content.Position = 15 * 1024 * 1024;
- baseFile.Content.WriteByte(0xFF);
+ baseFile.Content.Position = 15 * 1024 * 1024;
+ baseFile.Content.WriteByte(0xFF);
- diskContent = baseFile.Content;
+ fixed_diskContent = baseFile.Content;
+ }
}
[Fact]
@@ -54,7 +71,7 @@ public void BuildFixed()
var builder = new DiskBuilder
{
DiskType = FileType.Fixed,
- Content = diskContent
+ Content = fixed_diskContent
};
var fileSpecs = builder.Build("foo").ToArray();
@@ -78,7 +95,7 @@ public void BuildDynamic()
var builder = new DiskBuilder
{
DiskType = FileType.Dynamic,
- Content = diskContent
+ Content = dynamic_diskContent
};
var fileSpecs = builder.Build("foo").ToArray();
diff --git a/Tests/LibraryTests/Vhd/DiskImageFileTest.cs b/Tests/LibraryTests/Vhd/DiskImageFileTest.cs
index 2a66e556c..d58c3080b 100644
--- a/Tests/LibraryTests/Vhd/DiskImageFileTest.cs
+++ b/Tests/LibraryTests/Vhd/DiskImageFileTest.cs
@@ -66,7 +66,7 @@ public void GetParentLocations()
using (var diffFile = new DiskImageFile(diffStream))
{
var BasePath = @"E:\FOO\";
-#if NET461_OR_GREATER || NETSTANDARD || NETCOREAPP
+#if NET471_OR_GREATER || NETSTANDARD || NETCOREAPP
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
BasePath = "/foo/";
diff --git a/Tests/LibraryTests/Vhdx/DiskBuilderTest.cs b/Tests/LibraryTests/Vhdx/DiskBuilderTest.cs
index e1bfd6128..314656cd1 100644
--- a/Tests/LibraryTests/Vhdx/DiskBuilderTest.cs
+++ b/Tests/LibraryTests/Vhdx/DiskBuilderTest.cs
@@ -32,31 +32,49 @@ namespace LibraryTests.Vhdx;
public class DiskBuilderTest
{
- private SparseStream diskContent;
+ private SparseStream dynamicDiskContent;
+ private SparseStream fixedDiskContent;
public DiskBuilderTest()
{
- var sourceStream = new SparseMemoryStream();
- sourceStream.SetLength(160 * 1024L * 1024);
- for (var i = 0; i < 8; ++i)
{
- sourceStream.Position = i * 1024L * 1024;
- sourceStream.WriteByte((byte)i);
+ var sourceStream = new SparseMemoryStream();
+ sourceStream.SetLength(160 * 1024L * 1024);
+ for (var i = 0; i < 8; ++i)
+ {
+ sourceStream.Position = i * 1024L * 1024;
+ sourceStream.WriteByte((byte)i);
+ }
+
+ sourceStream.Position = 150 * 1024 * 1024;
+ sourceStream.WriteByte(0xFF);
+
+ dynamicDiskContent = sourceStream;
}
- sourceStream.Position = 150 * 1024 * 1024;
- sourceStream.WriteByte(0xFF);
+ {
+ var sourceStream = new SparseMemoryStream();
+ sourceStream.SetLength(160 * 1024L * 1024);
+ for (var i = 0; i < 8; ++i)
+ {
+ sourceStream.Position = i * 1024L * 1024;
+ sourceStream.WriteByte((byte)i);
+ }
+
+ sourceStream.Position = 150 * 1024 * 1024;
+ sourceStream.WriteByte(0xFF);
- diskContent = sourceStream;
+ fixedDiskContent = sourceStream;
+ }
}
- [Fact(Skip = "Fixed size Vhdx not implemeted")]
+ [Fact()]
public void BuildFixed()
{
var builder = new DiskBuilder
{
DiskType = DiskType.Fixed,
- Content = diskContent
+ Content = fixedDiskContent
};
var fileSpecs = builder.Build("foo").ToArray();
@@ -74,6 +92,23 @@ public void BuildFixed()
Assert.Equal(0xFF, disk.Content.ReadByte());
}
+ [Fact()]
+ public void BuildEmptyFixed()
+ {
+ var builder = new DiskBuilder
+ {
+ DiskType = DiskType.Fixed,
+ Content = new SparseMemoryStream()
+ };
+
+ var fileSpecs = builder.Build("foo").ToArray();
+ Assert.Single(fileSpecs);
+ Assert.Equal("foo.vhdx", fileSpecs[0].Name);
+
+ using var disk = new Disk(fileSpecs[0].OpenStream(), Ownership.Dispose);
+ Assert.Equal(0, disk.Content.Length);
+ }
+
[Fact]
public void BuildEmptyDynamic()
{
@@ -170,7 +205,7 @@ public void BuildDynamic()
var builder = new DiskBuilder
{
DiskType = DiskType.Dynamic,
- Content = diskContent
+ Content = dynamicDiskContent
};
var fileSpecs = builder.Build("foo").ToArray();
diff --git a/Tests/LibraryTests/Vhdx/DiskTest.cs b/Tests/LibraryTests/Vhdx/DiskTest.cs
index 5b7285c5d..abe941c12 100644
--- a/Tests/LibraryTests/Vhdx/DiskTest.cs
+++ b/Tests/LibraryTests/Vhdx/DiskTest.cs
@@ -36,10 +36,10 @@ public class DiskTest
public void InitializeFixed()
{
var ms = new MemoryStream();
- using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 8 * 1024 * 1024))
+ using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 8 * Sizes.OneMiB))
{
Assert.NotNull(disk);
- Assert.True(disk.Geometry.Value.Capacity is > (long)(7.5 * 1024 * 1024) and <= (8 * 1024 * 1024));
+ Assert.True(disk.Geometry.Value.Capacity is > (long)(7.5 * Sizes.OneMiB) and <= (8 * Sizes.OneMiB));
}
// Check the stream is still valid
@@ -47,11 +47,23 @@ public void InitializeFixed()
ms.Dispose();
}
+ [Fact]
+ public void InitializeFixedOwnStream()
+ {
+ var ms = new MemoryStream();
+ using (var disk = Disk.InitializeFixed(ms, Ownership.Dispose, 8 * Sizes.OneMiB))
+ {
+ }
+
+ Assert.Throws(() => ms.ReadByte());
+ }
+
+
[Fact]
public void InitializeDynamicOwnStream()
{
var ms = new MemoryStream();
- using (var disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 8 * 1024 * 1024))
+ using (var disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 8 * Sizes.OneMiB))
{
}
@@ -62,17 +74,17 @@ public void InitializeDynamicOwnStream()
public void InitializeDynamic()
{
var ms = new MemoryStream();
- using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 16 * 1024L * 1024 * 1024))
+ using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 16 * Sizes.OneGiB))
{
Assert.NotNull(disk);
- Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * 1024L * 1024 * 1024) and <= (16 * 1024L * 1024 * 1024));
+ Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * Sizes.OneGiB) and <= (16 * Sizes.OneGiB));
}
- Assert.True(8 * 1024 * 1024 > ms.Length);
+ Assert.True(8 * Sizes.OneMiB > ms.Length);
using (var disk = new Disk(ms, Ownership.Dispose))
{
- Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * 1024L * 1024 * 1024) and <= (16 * 1024L * 1024 * 1024));
+ Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * Sizes.OneGiB) and <= (16 * Sizes.OneGiB));
}
}
@@ -81,16 +93,16 @@ public void InitializeDifferencing()
{
var baseStream = new MemoryStream();
var diffStream = new MemoryStream();
- var baseFile = DiskImageFile.InitializeDynamic(baseStream, Ownership.Dispose, 16 * 1024L * 1024 * 1024);
+ var baseFile = DiskImageFile.InitializeDynamic(baseStream, Ownership.Dispose, 16 * Sizes.OneGiB);
using (var disk = Disk.InitializeDifferencing(diffStream, Ownership.None, baseFile, Ownership.Dispose, @"C:\TEMP\Base.vhd", @".\Base.vhd", DateTime.UtcNow))
{
Assert.NotNull(disk);
- Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * 1024L * 1024 * 1024) and <= (16 * 1024L * 1024 * 1024));
+ Assert.True(disk.Geometry.Value.Capacity is > (long)(15.8 * Sizes.OneGiB) and <= (16 * Sizes.OneGiB));
Assert.True(disk.Geometry.Value.Capacity == baseFile.Geometry.Capacity);
Assert.Equal(2, new List(disk.Layers).Count);
}
- Assert.True(8 * 1024 * 1024 > diffStream.Length);
+ Assert.True(8 * Sizes.OneMiB > diffStream.Length);
diffStream.Dispose();
}
@@ -99,7 +111,30 @@ public void ConstructorDynamic()
{
Geometry geometry;
var ms = new MemoryStream();
- using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 16 * 1024L * 1024 * 1024))
+ using (var disk = Disk.InitializeDynamic(ms, Ownership.None, 16 * Sizes.OneGiB))
+ {
+ geometry = disk.Geometry.Value;
+ }
+
+ using (var disk = new Disk(ms, Ownership.None))
+ {
+ Assert.Equal(geometry, disk.Geometry);
+ Assert.NotNull(disk.Content);
+ }
+
+ using (var disk = new Disk(ms, Ownership.Dispose))
+ {
+ Assert.Equal(geometry, disk.Geometry);
+ Assert.NotNull(disk.Content);
+ }
+ }
+
+ [Fact]
+ public void ConstructorFixed()
+ {
+ Geometry geometry;
+ var ms = new MemoryStream();
+ using (var disk = Disk.InitializeFixed(ms, Ownership.None, 16 * Sizes.OneMiB))
{
geometry = disk.Geometry.Value;
}
@@ -118,10 +153,10 @@ public void ConstructorDynamic()
}
[Fact]
- public void ConstructorFromFiles()
+ public void ConstructorFromFilesDynamic()
{
var baseStream = new MemoryStream();
- var baseFile = DiskImageFile.InitializeDynamic(baseStream, Ownership.Dispose, 16 * 1024L * 1024 * 1024);
+ var baseFile = DiskImageFile.InitializeDynamic(baseStream, Ownership.Dispose, 16 * Sizes.OneGiB);
var childStream = new MemoryStream();
var childFile = DiskImageFile.InitializeDifferencing(childStream, Ownership.Dispose, baseFile, @"C:\temp\foo.vhd", @".\foo.vhd", DateTime.Now);
@@ -133,4 +168,21 @@ public void ConstructorFromFiles()
Assert.NotNull(disk.Content);
}
+ [Fact]
+ public void ConstructorFromFilesFixed()
+ {
+ var baseStream = new MemoryStream();
+ var baseFile = DiskImageFile.InitializeFixed(baseStream, Ownership.Dispose, 16 * Sizes.OneMiB);
+
+ var childStream = new MemoryStream();
+ var childFile = DiskImageFile.InitializeDifferencing(childStream, Ownership.Dispose, baseFile, @"C:\temp\foo.vhd", @".\foo.vhd", DateTime.Now);
+
+ var grandChildStream = new MemoryStream();
+ var grandChildFile = DiskImageFile.InitializeDifferencing(grandChildStream, Ownership.Dispose, childFile, @"C:\temp\child1.vhd", @".\child1.vhd", DateTime.Now);
+
+ using var disk = new Disk(new DiskImageFile[] { grandChildFile, childFile, baseFile }, Ownership.Dispose);
+ Assert.NotNull(disk.Content);
+ }
+
+
}
diff --git a/Utilities/VHDXCreate/Program.cs b/Utilities/VHDXCreate/Program.cs
index c5aa8c441..3e37a28dc 100644
--- a/Utilities/VHDXCreate/Program.cs
+++ b/Utilities/VHDXCreate/Program.cs
@@ -116,7 +116,7 @@ protected override void DoRun()
return;
}
- // Create Fixed disk
+ // Create LeaveBlocksAllocated disk
using var fs = new FileStream(_destFile.Value, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete, bufferSize: 2 << 20);
Disk.InitializeFixed(fs, Ownership.None, DiskSize);
}
diff --git a/Utilities/VHDXDump/Program.cs b/Utilities/VHDXDump/Program.cs
index 1b00bd320..402a94273 100644
--- a/Utilities/VHDXDump/Program.cs
+++ b/Utilities/VHDXDump/Program.cs
@@ -66,7 +66,7 @@ protected override void DoRun()
Console.WriteLine(" Signature: {0:x8}", info.Signature);
Console.WriteLine(" Creator: {0:x8}", info.Creator);
Console.WriteLine(" Block Size: {0} (0x{0:X8})", info.BlockSize);
- Console.WriteLine("Leave Blocks Alloced: {0}", info.LeaveBlocksAllocated);
+ Console.WriteLine(" LeaveBlocksAllocated: {0}", info.IsFixedDisk);
Console.WriteLine(" Has Parent: {0}", info.HasParent);
Console.WriteLine(" Disk Size: {0} ({1} (0x{1:X8}))", Utilities.ApproximateDiskSize(info.DiskSize), info.DiskSize);
Console.WriteLine(" Logical Sector Size: {0} (0x{0:X8})", info.LogicalSectorSize);
diff --git a/workload-install.ps1 b/workload-install.ps1
new file mode 100644
index 000000000..fa0c0311c
--- /dev/null
+++ b/workload-install.ps1
@@ -0,0 +1,304 @@
+#
+# Copyright (c) Samsung Electronics. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
+#
+
+<#
+.SYNOPSIS
+Installs Tizen workload manifest.
+.DESCRIPTION
+Installs the WorkloadManifest.json and WorkloadManifest.targets files for Tizen to the dotnet sdk.
+.PARAMETER Version
+Use specific VERSION
+.PARAMETER DotnetInstallDir
+Dotnet SDK Location installed
+#>
+
+[cmdletbinding()]
+param(
+ [Alias('v')][string]$Version="",
+ [Alias('d')][string]$DotnetInstallDir="",
+ [Alias('t')][string]$DotnetTargetVersionBand="",
+ [Alias('u')][switch]$UpdateAllWorkloads
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = "Stop"
+$ProgressPreference = "SilentlyContinue"
+
+$ManifestBaseName = "Samsung.NET.Sdk.Tizen.Manifest"
+
+$LatestVersionMap = @{
+ "$ManifestBaseName-6.0.100" = "7.0.101";
+ "$ManifestBaseName-6.0.200" = "7.0.100-preview.13.6";
+ "$ManifestBaseName-6.0.300" = "8.0.133";
+ "$ManifestBaseName-6.0.400" = "8.0.140";
+ "$ManifestBaseName-7.0.100-preview.6" = "7.0.100-preview.6.14";
+ "$ManifestBaseName-7.0.100-preview.7" = "7.0.100-preview.7.20";
+ "$ManifestBaseName-7.0.100-rc.1" = "7.0.100-rc.1.22";
+ "$ManifestBaseName-7.0.100-rc.2" = "7.0.100-rc.2.24";
+ "$ManifestBaseName-7.0.100" = "7.0.103";
+ "$ManifestBaseName-7.0.200" = "7.0.105";
+ "$ManifestBaseName-7.0.300" = "7.0.120";
+ "$ManifestBaseName-7.0.400" = "8.0.141";
+ "$ManifestBaseName-8.0.100-alpha.1" = "7.0.104";
+ "$ManifestBaseName-8.0.100-preview.2" = "7.0.106";
+ "$ManifestBaseName-8.0.100-preview.3" = "7.0.107";
+ "$ManifestBaseName-8.0.100-preview.4" = "7.0.108";
+ "$ManifestBaseName-8.0.100-preview.5" = "7.0.110";
+ "$ManifestBaseName-8.0.100-preview.6" = "7.0.121";
+ "$ManifestBaseName-8.0.100-preview.7" = "7.0.122";
+ "$ManifestBaseName-8.0.100-rc.1" = "7.0.124";
+ "$ManifestBaseName-8.0.100-rc.2" = "7.0.125";
+ "$ManifestBaseName-8.0.100-rtm" = "7.0.127";
+ "$ManifestBaseName-8.0.100" = "8.0.144";
+ "$ManifestBaseName-8.0.200" = "8.0.145";
+ "$ManifestBaseName-8.0.300" = "8.0.149";
+ "$ManifestBaseName-9.0.100-alpha.1" = "8.0.134";
+ "$ManifestBaseName-9.0.100-preview.1" = "8.0.135";
+ "$ManifestBaseName-9.0.100-preview.2" = "8.0.137";
+}
+
+function New-TemporaryDirectory {
+ $parent = [System.IO.Path]::GetTempPath()
+ $name = [System.IO.Path]::GetRandomFileName()
+ New-Item -ItemType Directory -Path (Join-Path $parent $name)
+}
+
+function Ensure-Directory([string]$TestDir) {
+ Try {
+ New-Item -ItemType Directory -Path $TestDir -Force -ErrorAction stop | Out-Null
+ [io.file]::OpenWrite($(Join-Path -Path $TestDir -ChildPath ".test-write-access")).Close()
+ Remove-Item -Path $(Join-Path -Path $TestDir -ChildPath ".test-write-access") -Force
+ }
+ Catch [System.UnauthorizedAccessException] {
+ Write-Error "No permission to install. Try run with administrator mode."
+ }
+}
+
+function Get-LatestVersion([string]$Id) {
+ $attempts=3
+ $sleepInSeconds=3
+ do
+ {
+ try
+ {
+ $Response = Invoke-WebRequest -Uri https://api.nuget.org/v3-flatcontainer/$Id/index.json -UseBasicParsing | ConvertFrom-Json
+ return $Response.versions | Select-Object -Last 1
+ }
+ catch {
+ Write-Host "Id: $Id"
+ Write-Host "An exception was caught: $($_.Exception.Message)"
+ }
+
+ $attempts--
+ if ($attempts -gt 0) { Start-Sleep $sleepInSeconds }
+ } while ($attempts -gt 0)
+
+ if ($LatestVersionMap.ContainsKey($Id))
+ {
+ Write-Host "Return cached latest version."
+ return $LatestVersionMap.$Id
+ } else {
+ Write-Error "Wrong Id: $Id"
+ }
+}
+
+function Get-Package([string]$Id, [string]$Version, [string]$Destination, [string]$FileExt = "nupkg") {
+ $OutFileName = "$Id.$Version.$FileExt"
+ $OutFilePath = Join-Path -Path $Destination -ChildPath $OutFileName
+
+ if ($Id -match ".net[0-9]+$") {
+ $Id = $Id -replace (".net[0-9]+", "")
+ }
+
+ Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/$Id/$Version" -OutFile $OutFilePath
+
+ return $OutFilePath
+}
+
+function Install-Pack([string]$Id, [string]$Version, [string]$Kind) {
+ $TempZipFile = $(Get-Package -Id $Id -Version $Version -Destination $TempDir -FileExt "zip")
+ $TempUnzipDir = Join-Path -Path $TempDir -ChildPath "unzipped\$Id"
+
+ switch ($Kind) {
+ "manifest" {
+ Expand-Archive -Path $TempZipFile -DestinationPath $TempUnzipDir
+ New-Item -Path $TizenManifestDir -ItemType "directory" -Force | Out-Null
+ Copy-Item -Path "$TempUnzipDir\data\*" -Destination $TizenManifestDir -Force
+ }
+ {($_ -eq "sdk") -or ($_ -eq "framework")} {
+ Expand-Archive -Path $TempZipFile -DestinationPath $TempUnzipDir
+ if ( ($kind -eq "sdk") -and ($Id -match ".net[0-9]+$")) {
+ $Id = $Id -replace (".net[0-9]+", "")
+ }
+ $TargetDirectory = $(Join-Path -Path $DotnetInstallDir -ChildPath "packs\$Id\$Version")
+ New-Item -Path $TargetDirectory -ItemType "directory" -Force | Out-Null
+ Copy-Item -Path "$TempUnzipDir/*" -Destination $TargetDirectory -Recurse -Force
+ }
+ "template" {
+ $TargetFileName = "$Id.$Version.nupkg".ToLower()
+ $TargetDirectory = $(Join-Path -Path $DotnetInstallDir -ChildPath "template-packs")
+ New-Item -Path $TargetDirectory -ItemType "directory" -Force | Out-Null
+ Copy-Item $TempZipFile -Destination $(Join-Path -Path $TargetDirectory -ChildPath "$TargetFileName") -Force
+ }
+ }
+}
+
+function Remove-Pack([string]$Id, [string]$Version, [string]$Kind) {
+ switch ($Kind) {
+ "manifest" {
+ Remove-Item -Path $TizenManifestDir -Recurse -Force
+ }
+ {($_ -eq "sdk") -or ($_ -eq "framework")} {
+ $TargetDirectory = $(Join-Path -Path $DotnetInstallDir -ChildPath "packs\$Id\$Version")
+ Remove-Item -Path $TargetDirectory -Recurse -Force
+ }
+ "template" {
+ $TargetFileName = "$Id.$Version.nupkg".ToLower();
+ Remove-Item -Path $(Join-Path -Path $DotnetInstallDir -ChildPath "template-packs\$TargetFileName") -Force
+ }
+ }
+}
+
+function Install-TizenWorkload([string]$DotnetVersion)
+{
+ $VersionSplitSymbol = '.'
+ $SplitVersion = $DotnetVersion.Split($VersionSplitSymbol)
+
+ $CurrentDotnetVersion = [Version]"$($SplitVersion[0]).$($SplitVersion[1])"
+ $DotnetVersionBand = $SplitVersion[0] + $VersionSplitSymbol + $SplitVersion[1] + $VersionSplitSymbol + $SplitVersion[2][0] + "00"
+ $ManifestName = "$ManifestBaseName-$DotnetVersionBand"
+
+ if ($DotnetTargetVersionBand -eq "" -or $UpdateAllWorkloads.IsPresent) {
+ if ($CurrentDotnetVersion -ge "7.0")
+ {
+ $IsPreviewVersion = $DotnetVersion.Contains("-preview") -or $DotnetVersion.Contains("-rc") -or $DotnetVersion.Contains("-alpha")
+ if ($IsPreviewVersion -and ($SplitVersion.Count -ge 4)) {
+ $DotnetTargetVersionBand = $DotnetVersionBand + $SplitVersion[2].SubString(3) + $VersionSplitSymbol + $($SplitVersion[3])
+ $ManifestName = "$ManifestBaseName-$DotnetTargetVersionBand"
+ }
+ elseif ($DotnetVersion.Contains("-rtm") -and ($SplitVersion.Count -ge 3)) {
+ $DotnetTargetVersionBand = $DotnetVersionBand + $SplitVersion[2].SubString(3)
+ $ManifestName = "$ManifestBaseName-$DotnetTargetVersionBand"
+ }
+ else {
+ $DotnetTargetVersionBand = $DotnetVersionBand
+ }
+ }
+ else {
+ $DotnetTargetVersionBand = $DotnetVersionBand
+ }
+ }
+
+ # Check latest version of manifest.
+ if ($Version -eq "" -or $UpdateAllWorkloads.IsPresent) {
+ $Version = Get-LatestVersion -Id $ManifestName
+ }
+
+ # Check workload manifest directory.
+ $ManifestDir = Join-Path -Path $DotnetInstallDir -ChildPath "sdk-manifests" | Join-Path -ChildPath $DotnetTargetVersionBand
+ $TizenManifestDir = Join-Path -Path $ManifestDir -ChildPath "samsung.net.sdk.tizen"
+ $TizenManifestFile = Join-Path -Path $TizenManifestDir -ChildPath "WorkloadManifest.json"
+
+ # Check and remove already installed old version.
+ if (Test-Path $TizenManifestFile) {
+ $ManifestJson = $(Get-Content $TizenManifestFile | ConvertFrom-Json)
+ $OldVersion = $ManifestJson.version
+ if ($OldVersion -eq $Version) {
+ $DotnetWorkloadList = Invoke-Expression "& '$DotnetCommand' workload list | Select-String -Pattern '^tizen'"
+ if ($DotnetWorkloadList)
+ {
+ Write-Host "Tizen Workload $Version version is already installed."
+ Continue
+ }
+ }
+
+ Ensure-Directory $ManifestDir
+ Write-Host "Removing $ManifestName/$OldVersion from $ManifestDir..."
+ Remove-Pack -Id $ManifestName -Version $OldVersion -Kind "manifest"
+ $ManifestJson.packs.PSObject.Properties | ForEach-Object {
+ Write-Host "Removing $($_.Name)/$($_.Value.version)..."
+ Remove-Pack -Id $_.Name -Version $_.Value.version -Kind $_.Value.kind
+ }
+ }
+
+ Ensure-Directory $ManifestDir
+ $TempDir = $(New-TemporaryDirectory)
+
+ # Install workload manifest.
+ Write-Host "Installing $ManifestName/$Version to $ManifestDir..."
+ Install-Pack -Id $ManifestName -Version $Version -Kind "manifest"
+
+ # Download and install workload packs.
+ $NewManifestJson = $(Get-Content $TizenManifestFile | ConvertFrom-Json)
+ $NewManifestJson.packs.PSObject.Properties | ForEach-Object {
+ Write-Host "Installing $($_.Name)/$($_.Value.version)..."
+ Install-Pack -Id $_.Name -Version $_.Value.version -Kind $_.Value.kind
+ }
+
+ # Add tizen to the installed workload metadata.
+ # Featured version band for metadata does NOT include any preview specifier.
+ # https://github.com/dotnet/sdk/blob/main/documentation/general/workloads/user-local-workloads.md
+ New-Item -Path $(Join-Path -Path $DotnetInstallDir -ChildPath "metadata\workloads\$DotnetVersionBand\InstalledWorkloads\tizen") -Force | Out-Null
+ if (Test-Path $(Join-Path -Path $DotnetInstallDir -ChildPath "metadata\workloads\$DotnetVersionBand\InstallerType\msi")) {
+ New-Item -Path "HKLM:\SOFTWARE\Microsoft\dotnet\InstalledWorkloads\Standalone\x64\$DotnetTargetVersionBand\tizen" -Force | Out-Null
+ }
+
+ # Clean up
+ Remove-Item -Path $TempDir -Force -Recurse
+
+ Write-Host "Done installing Tizen workload $Version"
+}
+
+# Check dotnet install directory.
+if ($DotnetInstallDir -eq "") {
+ if ($Env:DOTNET_ROOT -And $(Test-Path "$Env:DOTNET_ROOT")) {
+ $DotnetInstallDir = $Env:DOTNET_ROOT
+ } else {
+ $DotnetInstallDir = Join-Path -Path $Env:Programfiles -ChildPath "dotnet"
+ }
+}
+if (-Not $(Test-Path "$DotnetInstallDir")) {
+ Write-Error "No installed dotnet '$DotnetInstallDir'."
+}
+
+# Check installed dotnet version
+$DotnetCommand = "$DotnetInstallDir\dotnet"
+if (Get-Command $DotnetCommand -ErrorAction SilentlyContinue)
+{
+ if ($UpdateAllWorkloads.IsPresent)
+ {
+ $InstalledDotnetSdks = Invoke-Expression "& '$DotnetCommand' --list-sdks | Select-String -Pattern '^6|^7'" | ForEach-Object {$_ -replace (" \[.*","")}
+ }
+ else
+ {
+ $InstalledDotnetSdks = Invoke-Expression "& '$DotnetCommand' --version"
+ }
+}
+else
+{
+ Write-Error "'$DotnetCommand' occurs an error."
+}
+
+if (-Not $InstalledDotnetSdks)
+{
+ Write-Host "`n.NET SDK version 6 or later is required to install Tizen Workload."
+}
+else
+{
+ foreach ($DotnetSdk in $InstalledDotnetSdks)
+ {
+ try {
+ Write-Host "`nCheck Tizen Workload for sdk $DotnetSdk"
+ Install-TizenWorkload -DotnetVersion $DotnetSdk
+ }
+ catch {
+ Write-Host "Failed to install Tizen Workload for sdk $DotnetSdk"
+ Write-Host "$_"
+ Continue
+ }
+ }
+}
+
+Write-Host "`nDone"