/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. */ using System; using System.Collections.Generic; using System.IO; using System.Text; using Utilities; namespace DiskAccessLibrary.VMDK { public class VirtualMachineDiskDescriptor { public int Version; public uint ContentID; public uint ParentContentID; public VirtualMachineDiskType DiskType; public List<VirtualMachineDiskExtentEntry> ExtentEntries; public string Adapter; public long Cylinders; public int TracksPerCylinder; // heads public int SectorsPerTrack; public VirtualMachineDiskDescriptor(List<string> lines) { ParseDescriptor(lines); } private void ParseDescriptor(List<string> lines) { ExtentEntries = new List<VirtualMachineDiskExtentEntry>(); foreach (string line in lines) { if (line.StartsWith("version", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); Version = Conversion.ToInt32(value); } else if (line.StartsWith("CID", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); ContentID = UInt32.Parse(value, System.Globalization.NumberStyles.HexNumber); } else if (line.StartsWith("ParentCID", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); ParentContentID = UInt32.Parse(value, System.Globalization.NumberStyles.HexNumber); } else if (line.StartsWith("createType", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); value = QuotedStringUtils.Unquote(value); DiskType = GetFromString(value); } else if (line.StartsWith("RW", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith("RDONLY", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith("NOACCESS", StringComparison.InvariantCultureIgnoreCase)) { VirtualMachineDiskExtentEntry entry = VirtualMachineDiskExtentEntry.ParseEntry(line); ExtentEntries.Add(entry); } else if (line.StartsWith("ddb.adapterType", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); value = QuotedStringUtils.Unquote(value); Adapter = value; } else if (line.StartsWith("ddb.geometry.sectors", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); value = QuotedStringUtils.Unquote(value); SectorsPerTrack = Conversion.ToInt32(value); } else if (line.StartsWith("ddb.geometry.heads", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); value = QuotedStringUtils.Unquote(value); TracksPerCylinder = Conversion.ToInt32(value); } else if (line.StartsWith("ddb.geometry.cylinders", StringComparison.InvariantCultureIgnoreCase)) { string value = line.Substring(line.IndexOf('=') + 1).Trim(); value = QuotedStringUtils.Unquote(value); Cylinders = Conversion.ToInt64(value); } } } public void UpdateExtentEntries(List<string> lines) { int startIndex = -1; // Remove previous extent entries for (int index = 0; index < lines.Count; index++) { string line = lines[index]; if (line.StartsWith("RW", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith("RDONLY", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith("NOACCESS", StringComparison.InvariantCultureIgnoreCase)) { if (startIndex == -1) { startIndex = index; } lines.RemoveAt(index); index--; } } if (startIndex >= 0) { foreach (VirtualMachineDiskExtentEntry entry in ExtentEntries) { lines.Insert(startIndex, entry.GetEntryLine()); startIndex++; } } } public static VirtualMachineDiskDescriptor ReadFromFile(string descriptorPath) { List<string> lines = ReadASCIITextLines(descriptorPath); if (lines == null) { return null; } return new VirtualMachineDiskDescriptor(lines); } private static VirtualMachineDiskType GetFromString(string createType) { switch (createType.ToLower()) { case "custom": return VirtualMachineDiskType.Custom; case "monolithicsparse": return VirtualMachineDiskType.MonolithicSparse; case "monolithicflat": return VirtualMachineDiskType.MonolithicFlat; case "2Gbmaxextentsparse": return VirtualMachineDiskType.TwoGbMaxExtentSparse; case "2Gbmaxextentflat": return VirtualMachineDiskType.TwoGbMaxExtentFlat; case "fulldevice": return VirtualMachineDiskType.FullDevice; case "partitioneddevice": return VirtualMachineDiskType.PartitionedDevice; case "vmfspreallocated": return VirtualMachineDiskType.VmfsPreallocated; case "vmfseagerzeroedthick": return VirtualMachineDiskType.VmfsEagerZeroedThick; case "vmfsthin": return VirtualMachineDiskType.VmfsThin; case "vmfssparse": return VirtualMachineDiskType.VmfsSparse; case "vmfsrdm": return VirtualMachineDiskType.VmfsRDM; case "vmfsRDMP": return VirtualMachineDiskType.VmfsRDMP; case "vmfsraw": return VirtualMachineDiskType.VmfsRaw; case "streamoptimized": return VirtualMachineDiskType.StreamOptimized; default: return VirtualMachineDiskType.Custom; } } public static List<string> ReadASCIITextLines(string path) { string text = ReadASCIIText(path); if (text == null) { return null; } return GetLines(text); } public static List<string> GetLines(string text) { List<string> result = new List<string>(); StringReader reader = new StringReader(text); string line; while ((line = reader.ReadLine()) != null) { result.Add(line); } return result; } public static string ReadASCIIText(string path) { StringBuilder builder = new StringBuilder(); FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read); int bufferSize = 0x1000; // default FileStream buffer size byte[] buffer = new byte[bufferSize]; int bytesRead = stream.Read(buffer, 0, buffer.Length); string temp = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead); while (bytesRead > 0) { foreach (char c in temp) { if (char.IsControl(c) && c != '\r' && c != '\n') { stream.Close(); return null; } } builder.Append(temp); if (bytesRead == bufferSize) { bytesRead = stream.Read(buffer, 0, buffer.Length); temp = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead); } else { break; } } stream.Close(); return builder.ToString(); } } }