/// <summary> /// /// </summary> /// <param name="OldVHD"></param> /// <param name="NewVHD"></param> /// <param name="Output">Filename to the output file. Method will fail if this already exists unless Force is passed as 'true'.</param> /// <param name="OutputType">A <see cref="VMProvisioningAgent.DiffVHD.DiskType"/> which specifies the output file format.</param> /// <param name="Force">If true, will overwrite the Output file if it already exists. Defaults to 'false'.</param> /// <param name="Partition">An int tuple which declares a specific pair of partitions to compare. The first value in the tuple will be the 0-indexed partition number from OldVHD to compare against. The second value of the tuple will be the 0-indexed parition from NewVHD to compare with.</param> /// <param name="Style"></param> /// <returns></returns> internal static void CreateDiff(string OldVHD, string NewVHD, string Output, DiskType OutputType = DiskType.VHD, bool Force = false, Tuple <int, int> Partition = null, ComparisonStyle Style = ComparisonStyle.DateTimeOnly) { if (File.Exists(Output) && !Force) { throw new ArgumentException("Output file already exists.", "Output"); } if (!File.Exists(OldVHD)) { throw new ArgumentException("Input file does not exist.", "OldVHD"); } if (!File.Exists(NewVHD)) { throw new ArgumentException("Input file does not exist.", "NewVHD"); } // byte[] CopyBuffer = new byte[1024*1024]; VirtualDisk Old, New, Out; Old = VirtualDisk.OpenDisk(OldVHD, FileAccess.Read); New = VirtualDisk.OpenDisk(NewVHD, FileAccess.Read); using (Old) using (New) using (var OutFS = new FileStream(Output, Force ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None)) { // Check type of filesystems being compared if (!Old.IsPartitioned) { throw new ArgumentException("Input disk is not partitioned.", "OldVHD"); } if (!New.IsPartitioned) { throw new ArgumentException("Input disk is not partitioned.", "NewVHD"); } long CapacityBuffer = 64 * Math.Max(Old.Geometry.BytesPerSector, New.Geometry.BytesPerSector); // starting with 64 sectors as a buffer for partition information in the output file long[] OutputCapacities = new long[Partition != null ? 1 : Old.Partitions.Count]; if (Partition != null) { var PartA = Old.Partitions[Partition.Item1]; var PartB = New.Partitions[Partition.Item2]; if (PartA.BiosType != PartB.BiosType) { throw new InvalidFileSystemException( String.Format( "Filesystem of partition {0} in '{1}' does not match filesystem type of partition {2} in '{3}'.", Partition.Item2, NewVHD, Partition.Item1, OldVHD)); } OutputCapacities[0] += Math.Max(PartA.SectorCount * Old.Geometry.BytesPerSector, PartB.SectorCount * New.Geometry.BytesPerSector); } else { if (Old.Partitions.Count != New.Partitions.Count) { throw new ArgumentException( "Input disks do not have the same number of partitions. To compare specific partitions on mismatched disks, provide the 'Partition' parameter."); } for (int i = 0; i < Old.Partitions.Count; i++) { if (Old.Partitions[i].BiosType != New.Partitions[i].BiosType) { throw new InvalidFileSystemException(String.Format("Filesystem of partition {0} in '{1}' does not match filesystem type of partition {0} in '{2}'.", i, NewVHD, OldVHD)); } else { OutputCapacities[i] = Math.Max(Old.Partitions[i].SectorCount * Old.Geometry.BytesPerSector, New.Partitions[i].SectorCount * New.Geometry.BytesPerSector); } } } long OutputCapacity = CapacityBuffer + OutputCapacities.Sum(); switch (OutputType) { case DiskType.VHD: Out = DiscUtils.Vhd.Disk.InitializeDynamic(OutFS, Ownership.None, OutputCapacity, Math.Max(New.BlockSize, 512 * 1024)); // the Max() is present only because there's currently a bug with blocksize < (8*sectorSize) in DiscUtils break; case DiskType.VHDX: Out = DiscUtils.Vhdx.Disk.InitializeDynamic(OutFS, Ownership.None, OutputCapacity, Math.Max(New.BlockSize, 512 * 1024)); break; default: throw new NotSupportedException("The selected disk type is not supported at this time.", new ArgumentException( "Selected DiskType not currently supported.", "OutputType")); } using (Out) { // set up the output location if (Out is DiscUtils.Vhd.Disk) { ((DiscUtils.Vhd.Disk)Out).AutoCommitFooter = false; } var OutParts = BiosPartitionTable.Initialize(Out); if (Partition != null) { OutParts.Create(GetPartitionType(Old.Partitions[Partition.Item1]), false); // there is no need (ever) for a VHD diff to have bootable partitions var OutFileSystem = Out.FormatPartition(0); DiffPart(DetectFileSystem(Old.Partitions[Partition.Item1]), DetectFileSystem(New.Partitions[Partition.Item2]), OutFileSystem, // As we made the partition spen the entire drive, it should be the only partition Style); } else // Partition == null { for (int i = 0; i < Old.Partitions.Count; i++) { var partIndex = OutParts.Create(Math.Max(Old.Partitions[i].SectorCount * Old.Parameters.BiosGeometry.BytesPerSector, New.Partitions[i].SectorCount * New.Parameters.BiosGeometry.BytesPerSector), GetPartitionType(Old.Partitions[i]), false); var OutFileSystem = Out.FormatPartition(partIndex); DiffPart(DetectFileSystem(Old.Partitions[i]), DetectFileSystem(New.Partitions[i]), OutFileSystem, Style); } } } // using (Out) }// using (Old, New, and OutFS) }