Example #1
0
 /// <summary>
 /// Exports the current header to a 1024 byte array
 /// </summary>
 /// <remarks>
 /// This will not validate the disk or update the header.
 /// Use <see cref="Validate"/> and <see cref="ComputeChecksum"/> before exporting
 /// </remarks>
 /// <returns>Raw header data</returns>
 public byte[] Export()
 {
     using (var MS = new MemoryStream())
     {
         using (var BW = new BinaryWriter(MS))
         {
             BW.Write(Encoding.Default.GetBytes(Cookie));
             BW.Write(Tools.ToNetwork(DataOffset));
             BW.Write(Tools.ToNetwork(TableOffset));
             BW.Write(Tools.ToNetwork((ushort)HeaderVersion.Major));
             BW.Write(Tools.ToNetwork((ushort)HeaderVersion.Minor));
             BW.Write(Tools.ToNetwork(MaxTableEntries));
             BW.Write(Tools.ToNetwork(BlockSize));
             BW.Write(Tools.ToNetwork(Checksum));
             BW.Write(ParentUniqueId.ToByteArray());
             BW.Write(Tools.ToNetwork((uint)VHD.ToDiskTimestamp(ParentTimeStamp)));
             BW.Write(Reserved1);
             BW.Write(Encoding.Unicode.GetBytes(ParentUnicodeName));
             foreach (var E in ParentLocatorEntries)
             {
                 BW.Write(E.Export());
             }
             BW.Write(Reserved2);
             BW.Flush();
             return(MS.ToArray());
         }
     }
 }
Example #2
0
        /// <summary>
        /// Reads a stream into a VHD header
        /// </summary>
        /// <param name="S">Stream</param>
        private void FromStream(Stream S)
        {
            Encoding E = Encoding.Default;

            //The comments indicate the offset from the header start
            using (var BR = new BinaryReader(S, E, true))
            {
                //0
                Cookie = E.GetString(BR.ReadBytes(8));
                //8
                Features = (VhdFeatures)Tools.ToNetwork(BR.ReadUInt32());
                //12
                FileFormatVersion = new Version(Tools.ToNetwork(BR.ReadUInt16()), Tools.ToNetwork(BR.ReadUInt16()));
                //16
                DataOffset = Tools.ToNetwork(BR.ReadUInt64());
                //24
                TimeStamp = VHD.FromDiskTimestamp(Tools.ToNetwork(BR.ReadInt32()));
                //28
                CreatorApplication = E.GetString(BR.ReadBytes(4));
                //32
                CreatorVersion = new Version(Tools.ToNetwork(BR.ReadUInt16()), Tools.ToNetwork(BR.ReadUInt16()));
                //36
                CreatorHostOS = E.GetString(BR.ReadBytes(4));
                //40
                OriginalSize = Tools.ToNetwork(BR.ReadUInt64());
                //48
                CurrentSize = Tools.ToNetwork(BR.ReadUInt64());
                //56
                DiskGeometry = new CHS(
                    Tools.ToNetwork(BR.ReadUInt16()),
                    BR.ReadByte(),
                    BR.ReadByte()
                    );
                //60
                DiskType = (VhdType)Tools.ToNetwork(BR.ReadUInt32());
                //64
                Checksum = Tools.ToNetwork(BR.ReadInt32());
                //68
                DiskId = new Guid(BR.ReadBytes(16));
                //84
                SavedState = BR.ReadByte() == 1;
                //85
                Reserved = BR.ReadBytes(RESERVED_FIELD_SIZE);
                //512
            }
        }
Example #3
0
        /// <summary>
        /// Reads a stream into a VHD header
        /// </summary>
        /// <param name="S">Stream</param>
        private void FromStream(Stream S)
        {
            Encoding E = Encoding.Default;

            //The comments indicate the offset from the header start
            using (var BR = new BinaryReader(S, E, true))
            {
                //0
                Cookie = E.GetString(BR.ReadBytes(8));
                //8
                DataOffset = Tools.ToNetwork(BR.ReadUInt64());
                //16
                TableOffset = Tools.ToNetwork(BR.ReadUInt64());
                //24
                HeaderVersion = new Version(Tools.ToNetwork(BR.ReadUInt16()), Tools.ToNetwork(BR.ReadUInt16()));
                //28
                MaxTableEntries = Tools.ToNetwork(BR.ReadUInt32());
                //32
                BlockSize = Tools.ToNetwork(BR.ReadUInt32());
                //36
                Checksum = Tools.ToNetwork(BR.ReadInt32());
                //40
                ParentUniqueId = new Guid(BR.ReadBytes(16));
                //56
                ParentTimeStamp = VHD.FromDiskTimestamp(Tools.ToNetwork(BR.ReadUInt32()));
                //60
                Reserved1 = BR.ReadBytes(RESERVED1_FIELD_SIZE);
                //64
                ParentUnicodeName = Encoding.Unicode.GetString(BR.ReadBytes(UNICODE_PARENT_LENGTH));
                //576
                ParentLocatorEntries = Enumerable
                                       .Range(0, LOCATOR_ENTRY_COUNT)
                                       .Select(m => new LocatorEntry(BR.ReadBytes(LocatorEntry.ENTRY_SIZE)))
                                       .ToArray();
                //768
                Reserved2 = BR.ReadBytes(RESERVED2_FIELD_SIZE);
                //1024
            }
        }
Example #4
0
        /// <summary>
        /// Exports the current header to a 512 byte array
        /// </summary>
        /// <remarks>
        /// This will not validate the disk or update the header.
        /// Use <see cref="Validate"/> and <see cref="ComputeChecksum"/> before exporting
        /// </remarks>
        /// <returns>Raw header data</returns>
        public byte[] Export()
        {
            using (var MS = new MemoryStream())
            {
                using (var BW = new BinaryWriter(MS))
                {
                    BW.Write(Encoding.Default.GetBytes(Cookie));
                    BW.Write(Tools.ToNetwork((uint)Features));
                    BW.Write(Tools.ToNetwork((ushort)FileFormatVersion.Major));
                    BW.Write(Tools.ToNetwork((ushort)FileFormatVersion.Minor));
                    BW.Write(Tools.ToNetwork(DataOffset));
                    BW.Write(Tools.ToNetwork((int)VHD.ToDiskTimestamp(TimeStamp)));

                    BW.Write(Encoding.Default.GetBytes(CreatorApplication));
                    BW.Write(Tools.ToNetwork((ushort)CreatorVersion.Major));
                    BW.Write(Tools.ToNetwork((ushort)CreatorVersion.Minor));
                    BW.Write(Encoding.Default.GetBytes(CreatorHostOS));

                    BW.Write(Tools.ToNetwork(OriginalSize));
                    BW.Write(Tools.ToNetwork(CurrentSize));

                    BW.Write(Tools.ToNetwork((ushort)DiskGeometry.Cylinders));
                    BW.Write((byte)DiskGeometry.Heads);
                    BW.Write((byte)DiskGeometry.SectorsPerTrack);

                    BW.Write(Tools.ToNetwork((uint)DiskType));

                    BW.Write(Tools.ToNetwork(Checksum));

                    BW.Write(DiskId.ToByteArray());
                    BW.Write((byte)(SavedState ? 1 : 0));
                    BW.Write(Reserved);

                    BW.Flush();

                    return(MS.ToArray());
                }
            }
        }
Example #5
0
        /// <summary>
        /// Validates the header and throws an exception if it's invalid
        /// </summary>
        public void Validate()
        {
            var E = Encoding.Default;

            if (Cookie == null || E.GetBytes(Cookie).Length != 8)
            {
                throw new ValidationException(nameof(Cookie), "Must be an 8 byte (ANSI) string");
            }
            if (DataOffset != DEFAULT_DATA_OFFSET)
            {
                throw new ValidationException(nameof(DataOffset), $"Is currently unused and must be set to {DEFAULT_DATA_OFFSET}");
            }
            if (TableOffset < HEADER_LENGTH + Footer.HEADER_LENGTH)
            {
                throw new ValidationException(nameof(TableOffset), $"Can't be less than the footer size plus the dynamic header size");
            }
            if (HeaderVersion == null)
            {
                throw new ValidationException(nameof(HeaderVersion), "Must be defined");
            }
            if (HeaderVersion.Major < ushort.MinValue || HeaderVersion.Minor < ushort.MinValue || HeaderVersion.Major > ushort.MaxValue || HeaderVersion.Minor > ushort.MaxValue)
            {
                throw new ValidationException(nameof(HeaderVersion), $"Major and minor must range from {ushort.MinValue}-{ushort.MaxValue}");
            }
            if (MaxTableEntries == 0)
            {
                throw new ValidationException(nameof(MaxTableEntries), "Can't be zero. It should be equal to the number of blocks in the disk");
            }
            if (BlockSize == 0)
            {
                throw new ValidationException(nameof(BlockSize), "Can't be zero.");
            }
            if (BlockSize % 512 != 0)
            {
                throw new ValidationException(nameof(BlockSize), "Must be a multiple of 512");
            }
            if (VHD.ToDiskTimestamp(ParentTimeStamp) < int.MinValue || VHD.ToDiskTimestamp(ParentTimeStamp) > int.MaxValue)
            {
                throw new ValidationException(nameof(ParentTimeStamp), $"Must be at most {int.MinValue} - {int.MaxValue} seconds away from 2000-01-01 00:00:00 UTC");
            }
            if (Reserved1 == null || Reserved1.Length != RESERVED1_FIELD_SIZE || Reserved1.Any(m => m > 0))
            {
                throw new ValidationException(nameof(Reserved1), $"Must be {RESERVED1_FIELD_SIZE} nullbytes");
            }
            if (Checksum != ComputeChecksum())
            {
                throw new ValidationException(nameof(Checksum), $"Wrong checksum. Expected: {ComputeChecksum()}");
            }

            if (ParentUnicodeName == null || Encoding.Unicode.GetBytes(ParentUnicodeName).Length != UNICODE_PARENT_LENGTH)
            {
                throw new ValidationException(nameof(ParentUnicodeName), $"Must be a {UNICODE_PARENT_LENGTH} byte UTF-16 string. Pad with nullbytes as needed");
            }
            if (ParentLocatorEntries == null || ParentLocatorEntries.Length != LOCATOR_ENTRY_COUNT)
            {
                throw new ValidationException(nameof(ParentLocatorEntries), $"Must contain {LOCATOR_ENTRY_COUNT} entries");
            }
            for (var i = 0; i < LOCATOR_ENTRY_COUNT; i++)
            {
                try
                {
                    ParentLocatorEntries[i].Validate();
                }
                catch (Exception ex)
                {
                    throw new ValidationException(nameof(ParentLocatorEntries), $"Element at index {i} failed to validate. See inner exception for details.", ex);
                }
            }
            if (Reserved2 == null || Reserved2.Length != RESERVED2_FIELD_SIZE || Reserved2.Any(m => m > 0))
            {
                throw new ValidationException(nameof(Reserved2), $"Must be {RESERVED2_FIELD_SIZE} nullbytes");
            }
        }
Example #6
0
        /// <summary>
        /// Validates the header and throws an exception if it's invalid
        /// </summary>
        public void Validate()
        {
            var E = Encoding.Default;

            if (Cookie == null || E.GetBytes(Cookie).Length != 8)
            {
                throw new ValidationException(nameof(Cookie), "Must be an 8 byte (ANSI) string");
            }
            if (!Enum.IsDefined(typeof(VhdFeatures), Features))
            {
                throw new ValidationException(nameof(Features), "Must be one or a combination of the defined enum values");
            }
            if (!Features.HasFlag(VhdFeatures.Reserved))
            {
                throw new ValidationException(nameof(Features), $"Must have '{nameof(VhdFeatures.Reserved)}' flag set");
            }
            if ((int)Features >= ((int)VhdFeatures.Reserved << 1))
            {
                throw new ValidationException(nameof(Features), "Must have undefined bits set to zero");
            }

            if (FileFormatVersion == null)
            {
                throw new ValidationException(nameof(FileFormatVersion), "Must be defined");
            }
            if (!Tools.InRange(ushort.MinValue, FileFormatVersion.Major, ushort.MaxValue))
            {
                throw new ValidationException(nameof(FileFormatVersion) + "." + nameof(Version.Major), $"Must be in the range of {ushort.MinValue}-{ushort.MaxValue}");
            }
            if (!Tools.InRange(ushort.MinValue, FileFormatVersion.Major, ushort.MaxValue))
            {
                throw new ValidationException(nameof(FileFormatVersion) + "." + nameof(Version.Minor), $"Must be in the range of {ushort.MinValue}-{ushort.MaxValue}");
            }

            if (DiskType == VhdType.FixedDisk && DataOffset != OFFSET_NONE)
            {
                throw new ValidationException(nameof(DataOffset), $"Must be set to ({nameof(OFFSET_NONE)}){OFFSET_NONE} for a fixed vhd type");
            }

            if (VHD.ToDiskTimestamp(TimeStamp) < int.MinValue || VHD.ToDiskTimestamp(TimeStamp) > int.MaxValue)
            {
                throw new ValidationException(nameof(TimeStamp), $"Must be at most {int.MinValue}-{int.MaxValue} seconds away from 2000-01-01 00:00:00 UTC");
            }

            if (CreatorApplication == null || E.GetBytes(CreatorApplication).Length != 4)
            {
                throw new ValidationException(nameof(CreatorApplication), "Must be a 4 byte (ANSI) string");
            }

            if (CreatorVersion == null)
            {
                throw new ValidationException(nameof(CreatorVersion), "Must be defined");
            }
            if (!Tools.InRange(ushort.MinValue, CreatorVersion.Major, ushort.MaxValue))
            {
                throw new ValidationException(nameof(CreatorVersion) + nameof(Version.Major), $"Must be in the range of {ushort.MinValue}-{ushort.MaxValue}");
            }
            if (!Tools.InRange(ushort.MinValue, CreatorVersion.Minor, ushort.MaxValue))
            {
                throw new ValidationException(nameof(CreatorVersion) + nameof(Version.Minor), $"Must be in the range of {ushort.MinValue}-{ushort.MaxValue}");
            }

            if (CreatorHostOS == null || E.GetBytes(CreatorHostOS).Length != 4)
            {
                throw new ValidationException(nameof(CreatorHostOS), "Is not a 4 byte (ANSI) string");
            }

            if (OriginalSize == 0)
            {
                throw new ValidationException(nameof(OriginalSize), "Can't be zero");
            }
            if (CurrentSize == 0)
            {
                throw new ValidationException(nameof(CurrentSize), $"Can't be zero");
            }
            if (CurrentSize % 512 != 0)
            {
                throw new ValidationException(nameof(CurrentSize), "Must be a multiple of 512");
            }

            if (DiskGeometry == null)
            {
                throw new ValidationException(nameof(DiskGeometry), "Is not defined");
            }
            if (DiskGeometry.Cylinders < 1 || DiskGeometry.Cylinders > CHS.MAX_CYLINDERS)
            {
                throw new ValidationException(nameof(DiskGeometry) + nameof(CHS.Cylinders), $"Must be in the range of 1-{CHS.MAX_CYLINDERS}");
            }
            if (DiskGeometry.Heads < 1 || DiskGeometry.Heads > CHS.MAX_HEADS)
            {
                throw new ValidationException(nameof(DiskGeometry) + nameof(CHS.Heads), $"Must be in the range of 1-{CHS.MAX_HEADS}");
            }
            if (DiskGeometry.SectorsPerTrack < 1 || DiskGeometry.SectorsPerTrack > CHS.MAX_SECTORS_PER_TRACK)
            {
                throw new ValidationException(nameof(DiskGeometry) + nameof(CHS.SectorsPerTrack), $"Must be in the range of 1-{CHS.MAX_SECTORS_PER_TRACK}");
            }
            if (!DiskGeometry.Equals(new CHS(CurrentSize)))
            {
                throw new ValidationException(nameof(DiskGeometry), $"Does not matches {nameof(CurrentSize)}");
            }


            if (!Enum.IsDefined(typeof(VhdType), DiskType))
            {
                throw new ValidationException(nameof(DiskType), "Not one of the defined enum values");
            }

            if (Checksum != ComputeChecksum())
            {
                throw new ValidationException(nameof(Checksum), $"Wrong checksum. Expected: {ComputeChecksum()}");
            }

            if (DiskId == Guid.Empty)
            {
                throw new ValidationException(nameof(DiskId), "Is invalid (All zeros)");
            }
            if (Reserved == null || Reserved.Length != RESERVED_FIELD_SIZE)
            {
                throw new ValidationException(nameof(Reserved), $"Must be {RESERVED_FIELD_SIZE} bytes long");
            }
            if (Reserved.Any(m => m != 0))
            {
                throw new ValidationException(nameof(Reserved), "Must be made up of nullbytes only");
            }
        }