Browse Source

Updated DiskAccessLibrary

Tal Aloni 5 years ago
parent
commit
d6dbb64e4b

+ 2 - 3
DiskAccessLibrary/Disks/RawDiskImage/RawDiskImage.cs

@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+/* Copyright (C) 2014-2018 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,
@@ -7,7 +7,6 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text;
 using Utilities;
 
 namespace DiskAccessLibrary
@@ -74,7 +73,7 @@ namespace DiskAccessLibrary
             // Note: KB99794 provides information about FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING.
             // We must avoid using buffered writes, using it will negatively affect the performance and reliability.
             // Note: once the file system write buffer is filled, Windows may delay any (buffer-dependent) pending write operations, which will create a deadlock.
-            FileStream stream = new FileStream(this.Path, FileMode.Open, fileAccess, FileShare.Read, 0x1000, FILE_FLAG_NO_BUFFERING | FileOptions.WriteThrough);
+            FileStream stream = new FileStream(this.Path, FileMode.Open, fileAccess, FileShare.Read, m_bytesPerSector, FILE_FLAG_NO_BUFFERING | FileOptions.WriteThrough);
             return stream;
         }
 

+ 71 - 8
DiskAccessLibrary/Disks/VHD/VirtualHardDisk.Dynamic.cs

@@ -17,12 +17,12 @@ namespace DiskAccessLibrary
         private byte[] ReadSectorsFromDynamicDisk(long sectorIndex, int sectorCount)
         {
             byte[] buffer = new byte[sectorCount * BytesPerDiskSector];
+            int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / BytesPerDiskSector);
             int sectorOffset = 0;
             while (sectorOffset < sectorCount)
             {
                 uint blockIndex = (uint)((sectorIndex + sectorOffset) * BytesPerDiskSector / m_dynamicHeader.BlockSize);
                 int sectorOffsetInBlock = (int)(((sectorIndex + sectorOffset) * BytesPerDiskSector % m_dynamicHeader.BlockSize) / BytesPerDiskSector);
-                int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / BytesPerDiskSector);
                 int sectorsRemainingInBlock = sectorsInBlock - sectorOffsetInBlock;
                 int sectorsToRead = Math.Min(sectorCount - sectorOffset, sectorsRemainingInBlock);
 
@@ -40,6 +40,38 @@ namespace DiskAccessLibrary
             return buffer;
         }
 
+        public bool AreSectorsInUse(long sectorIndex, int sectorCount)
+        {
+            if (m_vhdFooter.DiskType != VirtualHardDiskType.Fixed)
+            {
+                int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / BytesPerDiskSector);
+                int sectorOffset = 0;
+                while (sectorOffset < sectorCount)
+                {
+                    uint blockIndex = (uint)((sectorIndex + sectorOffset) * BytesPerDiskSector / m_dynamicHeader.BlockSize);
+                    int sectorOffsetInBlock = (int)(((sectorIndex + sectorOffset) * BytesPerDiskSector % m_dynamicHeader.BlockSize) / BytesPerDiskSector);
+                    int sectorsRemainingInBlock = sectorsInBlock - sectorOffsetInBlock;
+                    int sectorsToRead = Math.Min(sectorCount - sectorOffset, sectorsRemainingInBlock);
+
+                    uint blockStartSector;
+                    if (m_blockAllocationTable.IsBlockInUse(blockIndex, out blockStartSector))
+                    {
+                        byte[] bitmap = ReadBlockUsageBitmap(blockIndex);
+                        if (!AreSectorsInUse(bitmap, sectorOffsetInBlock, sectorsToRead))
+                        {
+                            return false;
+                        }
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                    sectorOffset += sectorsToRead;
+                }
+            }
+            return true;
+        }
+
         private byte[] ReadBlockUsageBitmap(uint blockIndex)
         {
             if (m_vhdFooter.DiskType != VirtualHardDiskType.Fixed)
@@ -66,13 +98,13 @@ namespace DiskAccessLibrary
 
         private void WriteSectorsToDynamicDisk(long sectorIndex, byte[] data)
         {
-            int sectorOffset = 0;
             int sectorCount = data.Length / BytesPerDiskSector;
+            int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / BytesPerDiskSector);
+            int sectorOffset = 0;
             while (sectorOffset < sectorCount)
             {
                 uint blockIndex = (uint)((sectorIndex + sectorOffset) * BytesPerDiskSector / m_dynamicHeader.BlockSize);
                 int sectorOffsetInBlock = (int)(((sectorIndex + sectorOffset) * BytesPerDiskSector % m_dynamicHeader.BlockSize) / BytesPerDiskSector);
-                int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / BytesPerDiskSector);
                 int sectorsRemainingInBlock = sectorsInBlock - sectorOffsetInBlock;
                 int sectorsToWrite = Math.Min(sectorCount - sectorOffset, sectorsRemainingInBlock);
 
@@ -143,6 +175,38 @@ namespace DiskAccessLibrary
             }
         }
 
+        private static bool AreSectorsInUse(byte[] bitmap, int sectorOffsetInBitmap, int sectorCount)
+        {
+            int leadingBits = (8 - (sectorOffsetInBitmap % 8)) % 8;
+            for (int sectorOffset = 0; sectorOffset < leadingBits; sectorOffset++)
+            {
+                if (!IsSectorInUse(bitmap, sectorOffsetInBitmap + sectorOffset))
+                {
+                    return false;
+                }
+            }
+
+            int byteCount = Math.Max(sectorCount - leadingBits, 0) / 8;
+            int byteOffsetInBitmap = (sectorOffsetInBitmap + leadingBits) / 8;
+            for (int byteOffset = 0; byteOffset < byteCount; byteOffset++)
+            {
+                if (bitmap[byteOffsetInBitmap + byteOffset] != 0xFF)
+                {
+                    return false;
+                }
+            }
+
+            for (int sectorOffset = leadingBits + byteCount * 8; sectorOffset < sectorCount; sectorOffset++)
+            {
+                if (!IsSectorInUse(bitmap, sectorOffsetInBitmap + sectorOffset))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
         private static bool IsSectorInUse(byte[] bitmap, int sectorOffsetInBitmap)
         {
             int byteOffset = sectorOffsetInBitmap / 8;
@@ -178,14 +242,13 @@ namespace DiskAccessLibrary
         /// <exception cref="System.UnauthorizedAccessException"></exception>
         public static VirtualHardDisk CreateDynamicDisk(string path, long diskSize)
         {
-            const int BlockSizeInBytes = 4096 * BytesPerDiskSector;
-
-            if (diskSize % BlockSizeInBytes > 0)
+            if (diskSize % BytesPerDiskSector > 0)
             {
-                // All blocks within a given image must be the same size
-                throw new ArgumentException("Dynamic VHD disk size must be a multiple of 2MiB");
+                throw new ArgumentException("diskSize must be a multiple of sector size");
             }
 
+            const int BlockSizeInBytes = 4096 * BytesPerDiskSector;
+
             VHDFooter footer = new VHDFooter();
             footer.OriginalSize = (ulong)diskSize;
             footer.CurrentSize = (ulong)diskSize;

+ 6 - 1
DiskAccessLibrary/Disks/VHD/VirtualHardDisk.cs

@@ -246,8 +246,13 @@ namespace DiskAccessLibrary
         /// <param name="diskSize">In bytes</param>
         /// <exception cref="System.IO.IOException"></exception>
         /// <exception cref="System.UnauthorizedAccessException"></exception>
-        public static VirtualHardDisk Create(string path, long diskSize)
+        public static VirtualHardDisk CreateFixedDisk(string path, long diskSize)
         {
+            if (diskSize % BytesPerDiskSector > 0)
+            {
+                throw new ArgumentException("diskSize must be a multiple of sector size");
+            }
+
             VHDFooter footer = new VHDFooter();
             footer.OriginalSize = (ulong)diskSize;
             footer.CurrentSize = (ulong)diskSize;

+ 10 - 1
DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/AttributeListEntry.cs

@@ -1,4 +1,4 @@
-/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+/* Copyright (C) 2014-2018 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,
@@ -6,6 +6,7 @@
  */
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Text;
 using Utilities;
 
@@ -28,6 +29,14 @@ namespace DiskAccessLibrary.FileSystems.NTFS
         {
             AttributeType = (AttributeType)LittleEndianConverter.ToUInt32(buffer, offset + 0x00);
             Length = LittleEndianConverter.ToUInt16(buffer, offset + 0x04);
+            if (Length < AttributeListEntry.HeaderLength)
+            {
+                throw new InvalidDataException("Invalid attribute list entry, data length is less than the valid minimum");
+            }
+            else if (Length > buffer.Length - offset)
+            {
+                throw new InvalidDataException("Invalid attribute list entry, data length exceed list length");
+            }
             NameLength = buffer[offset + 0x06];
             NameOffset = buffer[offset + 0x07];
             LowestVCN = LittleEndianConverter.ToUInt64(buffer, offset + 0x08);

+ 1 - 8
DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/AttributeListRecord.cs

@@ -1,4 +1,4 @@
-/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+/* Copyright (C) 2014-2018 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,
@@ -7,7 +7,6 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text;
 using Utilities;
 
 namespace DiskAccessLibrary.FileSystems.NTFS
@@ -36,12 +35,6 @@ namespace DiskAccessLibrary.FileSystems.NTFS
                 AttributeListEntry entry = new AttributeListEntry(data, position);
                 AttributeList.Add(entry);
                 position += entry.Length;
-                
-                if (entry.Length < AttributeListEntry.HeaderLength)
-                {
-                    string message = String.Format("Invalid attribute list entry, data length: {0}, position: {1}", data.Length, position);
-                    throw new InvalidDataException(message);
-                }
             }
         }
 

+ 43 - 1
DiskAccessLibrary/FileSystems/NTFS/NTFSVolume.cs

@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+/* Copyright (C) 2014-2018 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,
@@ -176,6 +176,48 @@ namespace DiskAccessLibrary.FileSystems.NTFS
             return result;
         }
 
+        /// <summary>
+        /// This method is slower and should only be used for recovery purposes.
+        /// </summary>
+        public List<FileRecord> GetFileRecordsInDirectoryFromMft(long directoryBaseSegmentNumber)
+        {
+            return GetFileRecordsInDirectoryFromMft(directoryBaseSegmentNumber, false);
+        }
+
+        /// <summary>
+        /// This method is slower and should only be used for recovery purposes.
+        /// </summary>
+        private List<FileRecord> GetFileRecordsInDirectoryFromMft(long directoryBaseSegmentNumber, bool includeMetaFiles)
+        {
+            long maxNumOfRecords = m_mft.GetMaximumNumberOfSegments();
+            List<FileRecord> result = new List<FileRecord>();
+
+            for (long index = 0; index < maxNumOfRecords; index++)
+            {
+                FileRecord record;
+                try
+                {
+                    record = m_mft.GetFileRecord(index);
+                }
+                catch (InvalidDataException)
+                {
+                    continue;
+                }
+                if (record != null)
+                {
+                    if (record.ParentDirMftSegmentNumber == directoryBaseSegmentNumber)
+                    {
+                        if (record.IsInUse && (includeMetaFiles || !record.IsMetaFile))
+                        {
+                            result.Add(record);
+                        }
+                    }
+                }
+            }
+
+            return result;
+        }
+
         // logical cluster
         public byte[] ReadCluster(long clusterLCN)
         {

+ 2 - 2
DiskAccessLibrary/Properties/AssemblyInfo.cs

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
 //
 // You can specify all the values or you can default the Revision and Build Numbers 
 // by using the '*' as shown below:
-[assembly: AssemblyVersion("1.4.8.0")]
-[assembly: AssemblyFileVersion("1.4.8.0")]
+[assembly: AssemblyVersion("1.4.9.0")]
+[assembly: AssemblyFileVersion("1.4.9.0")]

+ 7 - 0
DiskAccessLibrary/RevisionHistory.txt

@@ -116,3 +116,10 @@ Revision History:
         VHD: DynamicDiskHeader: Improved implementation.
         VHD: Implemented dynamic VHD write functionality.
         Minor changes and improvements.
+
+1.4.9 - NTFS: AttributeListEntry: throw InvalidDataException when length is invalid.
+        NTFS: NTFSVolume: Added GetFileRecordsInDirectoryFromMft() method for recovery purposes.
+        VHD: CreateDynamicDisk now accepts any multiple of sector size as disk size.
+        VHD: Create renamed to CreateFixedDisk, checks that disk size is a multiple of sector size.
+        VHD: Added AreSectorsInUse() method.
+        RawDiskImage: Circumvented FileStream buffering which resulted in reading more sectors than required.

+ 1 - 1
ISCSIConsole/CreateDiskImageForm.cs

@@ -41,7 +41,7 @@ namespace ISCSIConsole
                 DiskImage diskImage;
                 try
                 {
-                    diskImage = VirtualHardDisk.Create(path, size);
+                    diskImage = VirtualHardDisk.CreateFixedDisk(path, size);
                 }
                 catch (IOException ex)
                 {