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"