├── .gitattributes ├── .gitignore ├── DiskAccessLibrary.Tests ├── DiskAccessLibrary.Tests.csproj ├── Helpers │ ├── ChkdskHelper.cs │ ├── MountHelper.cs │ └── VHDMountHelper.cs ├── IntegrationTests │ └── FileSystems │ │ └── NTFS │ │ ├── NTFSFormatTests.cs │ │ ├── NTFSLogTests.cs │ │ └── NTFSVolumeTests.cs ├── TestData │ └── VMDK │ │ ├── MonolithicSparse.vmdk │ │ └── StreamOptimized.vmdk └── UnitTests │ ├── Disks │ └── VMDK │ │ └── VirtualMachineDiskTests.cs │ ├── FileRecordTests.cs │ └── RawDiskImageTests.cs ├── DiskAccessLibrary.Win32 ├── DiskAccessLibrary.Win32.csproj ├── Disks │ ├── PhysicalDisk.cs │ └── PhysicalDiskHandlePool.cs ├── Enums │ └── LockStatus.cs ├── Helpers │ ├── LockHelper.cs │ ├── PhysicalDiskHelper.cs │ ├── WindowsVolumeHelper.cs │ └── WindowsVolumeManager.cs ├── LogicalDiskManager │ ├── DiskLockHelper.cs │ ├── DiskOfflineHelper.cs │ ├── LockHelper.cs │ ├── LockManager.cs │ ├── PhysicalDiskGroupDatabase.cs │ ├── WindowsDynamicDiskHelper.cs │ └── WindowsDynamicVolumeHelper.cs ├── Utilities │ ├── DeviceInterfaceUtils.cs │ ├── PhysicalDiskControl.cs │ └── VolumeControl.cs └── Volumes │ ├── OperatingSystemVolume.cs │ └── VolumeHandlePool.cs ├── DiskAccessLibrary.sln ├── DiskAccessLibrary ├── BaseClasses │ ├── Disk.cs │ ├── DiskExtent.cs │ ├── IDiskGeometry.cs │ └── Volume.cs ├── DiskAccessLibrary.csproj ├── Disks │ ├── DiskImage.cs │ ├── RAMDisk.cs │ ├── RawDiskImage │ │ └── RawDiskImage.cs │ ├── VHD │ │ ├── BlockAllocationTable.cs │ │ ├── DynamicDiskHeader.cs │ │ ├── ParentLocatorEntry.cs │ │ ├── VHDFooter.cs │ │ ├── VirtualHardDisk.Dynamic.cs │ │ ├── VirtualHardDisk.cs │ │ └── VirtualHardDiskType.cs │ └── VMDK │ │ ├── Enums │ │ ├── ExtentType.cs │ │ ├── SparseExtentCompression.cs │ │ ├── SparseExtentHeaderFlags.cs │ │ └── VirtualMachineDiskType.cs │ │ ├── SparseExtent.cs │ │ ├── SparseExtentHeader.cs │ │ ├── StreamOptimizedWriteOnlySparseExtent.cs │ │ ├── VirtualMachineDisk.Create.cs │ │ ├── VirtualMachineDisk.cs │ │ ├── VirtualMachineDiskDescriptor.cs │ │ ├── VirtualMachineDiskExtentEntry.cs │ │ └── ZLibCompressionHelper.cs ├── Exceptions │ ├── AlreadyExistsException.cs │ ├── CyclicRedundancyCheckException.cs │ ├── DeviceNotReadyException.cs │ ├── DirectoryNotEmptyException.cs │ ├── DiskFullException.cs │ ├── InvalidNameException.cs │ ├── InvalidPathException.cs │ └── SharingViolationException.cs ├── FileSystems │ ├── Abstractions │ │ ├── FileSystem.cs │ │ ├── FileSystemEntry.cs │ │ └── IFileSystem.cs │ ├── FileSystemHelper.cs │ ├── IExtendableFileSystem.cs │ └── NTFS │ │ ├── Adapters │ │ ├── NTFSFileStream.cs │ │ └── NTFSFileSystem.cs │ │ ├── AttributeData │ │ ├── AttributeData.cs │ │ ├── AttributeList.cs │ │ ├── BitmapData.cs │ │ ├── IndexData.FileName.cs │ │ ├── IndexData.cs │ │ └── NonResidentAttributeData.cs │ │ ├── AttributeDefinition.cs │ │ ├── AttributeRecord │ │ ├── AttributeRecord.cs │ │ ├── FileNameAttributeRecord.cs │ │ ├── IndexAllocationRecord.cs │ │ ├── IndexRootRecord.cs │ │ ├── NonResidentAttributeRecord.cs │ │ ├── ResidentAttributeRecord.cs │ │ ├── StandardInformationRecord.cs │ │ ├── VolumeInformationRecord.cs │ │ └── VolumeNameRecord.cs │ │ ├── Enums │ │ ├── AttributeDefinitionFlags.cs │ │ ├── AttributeFlags.cs │ │ ├── AttributeForm.cs │ │ ├── AttributeType.cs │ │ ├── CollationRule.cs │ │ ├── ContentType.cs │ │ ├── FileAttributes.cs │ │ ├── FileNameFlags.cs │ │ ├── FileRecordFlags.cs │ │ ├── IndexEntryFlags.cs │ │ ├── IndexHeaderFlags.cs │ │ ├── ResidentForm.cs │ │ └── VolumeFlags.cs │ │ ├── FileRecord │ │ ├── FileRecord.cs │ │ ├── FileRecordHelper.cs │ │ ├── FileRecordSegment.cs │ │ └── MultiSectorHelper.cs │ │ ├── Index │ │ ├── CollationHelper.cs │ │ ├── DosFileNameHelper.cs │ │ ├── IndexEntry.cs │ │ ├── IndexHeader.cs │ │ ├── IndexHelper.cs │ │ └── IndexRecord.cs │ │ ├── Log │ │ ├── Enums │ │ │ ├── LfsRecordFlags.cs │ │ │ ├── LfsRecordPageFlags.cs │ │ │ ├── LfsRecordType.cs │ │ │ ├── LfsRestartFlags.cs │ │ │ ├── NTFSLogOperation.cs │ │ │ └── TransactionState.cs │ │ ├── LogFile.Analysis.cs │ │ ├── LogFile.cs │ │ ├── NTFSLogClient.Analysis.cs │ │ ├── NTFSLogClient.cs │ │ ├── RestartTables │ │ │ ├── AttributeNameEntry.cs │ │ │ ├── DirtyPageEntry.cs │ │ │ ├── OpenAttributeEntry.cs │ │ │ ├── RestartTableEntry.cs │ │ │ ├── RestartTableHeader.cs │ │ │ ├── RestartTableHelper.cs │ │ │ └── TransactionEntry.cs │ │ └── Structures │ │ │ ├── BitmapRange.cs │ │ │ ├── LfsClientRecord.cs │ │ │ ├── LfsRecord.cs │ │ │ ├── LfsRecordPage.cs │ │ │ ├── LfsRestartArea.cs │ │ │ ├── LfsRestartPage.cs │ │ │ ├── NTFSLogRecord.cs │ │ │ └── NTFSRestartRecord.cs │ │ ├── MasterFileTable.cs │ │ ├── NTFSBootRecord.cs │ │ ├── NTFSFile.cs │ │ ├── NTFSVolume.Extend.cs │ │ ├── NTFSVolume.Recovery.cs │ │ ├── NTFSVolume.cs │ │ ├── NTFSVolumeCreator.cs │ │ ├── Structures │ │ ├── AttributeDefinitionEntry.cs │ │ ├── AttributeListEntry.cs │ │ ├── DataRun.cs │ │ ├── DataRunSequence.cs │ │ ├── DuplicatedInformation.cs │ │ ├── FileNameRecord.cs │ │ ├── MftSegmentReference.cs │ │ └── MultiSectorHeader.cs │ │ └── VolumeBitmap.cs ├── Helpers │ ├── BasicDiskHelper.cs │ ├── DiskExtentHelper.cs │ ├── DiskExtentsHelper.cs │ ├── ExtendHelper.Volume.cs │ ├── Settings.cs │ └── VolumeHelper.cs ├── LogicalDiskManager │ ├── DatabaseRecords │ │ ├── ComponentRecord.cs │ │ ├── DatabaseRecord.cs │ │ ├── DatabaseRecordFragment.cs │ │ ├── DiskGroupRecord.cs │ │ ├── DiskRecord.cs │ │ ├── ExtentRecord.cs │ │ └── VolumeRecord.cs │ ├── DiskGroupDatabase.cs │ ├── DynamicDisk.cs │ ├── DynamicDiskExtent.cs │ ├── Enums │ │ ├── DatabaseHeaderUpdateStatus.cs │ │ ├── DatabaseRecordUpdateStatus.cs │ │ ├── ExtentLayoutName.cs │ │ ├── KernelUpdateLogEntryStatus.cs │ │ ├── ReadPolicyName.cs │ │ ├── RecordType.cs │ │ └── VolumeFlags.cs │ ├── Exceptions │ │ └── MissingDatabaseRecordException.cs │ ├── Helpers │ │ ├── DynamicDiskExtentHelper.cs │ │ ├── DynamicDiskExtentsHelper.cs │ │ ├── DynamicDiskHelper.Extents.cs │ │ ├── DynamicDiskHelper.cs │ │ ├── DynamicVolumeHelper.cs │ │ ├── PrivateRegionHelper.cs │ │ ├── PublicRegionHelper.cs │ │ ├── RetainHelper.cs │ │ └── VolumeManagerDatabaseHelper.cs │ ├── KernelUpdateLog │ │ ├── KernalUpdateLog.cs │ │ └── KernelUpdateLogPage.cs │ ├── PrivateHeader.cs │ ├── TOCBlock │ │ ├── TOCBlock.cs │ │ └── TOCRegion.cs │ ├── VolumeManagerDatabase.cs │ ├── VolumeManagerDatabaseCopy.cs │ ├── VolumeManagerDatabaseHeader.cs │ └── Volumes │ │ ├── DynamicColumn.cs │ │ ├── DynamicVolume.cs │ │ ├── MirroredVolume.cs │ │ ├── Raid5Volume.cs │ │ ├── SimpleVolume.cs │ │ ├── SpannedVolume.cs │ │ └── StripedVolume.cs ├── PartitionTables │ ├── GuidPartitionTable │ │ ├── GuidPartitionEntry.cs │ │ ├── GuidPartitionEntryCollection.cs │ │ ├── GuidPartitionTable.cs │ │ └── GuidPartitionTableHeader.cs │ └── MasterBootRecord │ │ ├── CHSAddress.cs │ │ ├── MasterBootRecord.cs │ │ ├── PartitionTableEntry.cs │ │ └── PartitionTypeName.cs ├── RevisionHistory.txt ├── Volumes │ ├── GPTPartition.cs │ ├── MBRPartition.cs │ ├── Partition.cs │ └── RemovableVolume.cs └── Win32 │ ├── Enums │ └── Win32Error.cs │ ├── Helpers │ └── IOExceptionHelper.cs │ └── Utilities │ ├── FileStreamEx.cs │ ├── FileStreamUtils.cs │ ├── HandleUtils.cs │ └── SecurityUtils.cs ├── License.txt ├── Publish.bat ├── Readme.md └── Utilities ├── ByteUtils ├── BigEndianReader.cs ├── BigEndianWriter.cs ├── ByteReader.cs ├── ByteUtils.cs ├── ByteWriter.cs ├── LittleEndianReader.cs └── LittleEndianWriter.cs ├── Comparers └── ReverseComparer.cs ├── Conversion ├── BigEndianConverter.cs ├── Conversion.SimpleTypes.cs └── LittleEndianConverter.cs ├── Cryptography ├── AesCcm.cs ├── AesCmac.cs └── CRC32.cs ├── Generics ├── BlockingQueue.cs ├── KeyValuePairList.Sort.cs ├── KeyValuePairList.cs ├── Map.cs ├── Reference.cs └── SortedList.cs ├── Strings └── QuotedStringUtils.cs ├── Threading ├── CountdownLatch.cs └── Parallel.cs └── Utilities.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Unsetting the text attribute on a path tells Git not to attempt any end-of-line conversion upon checkin or checkout 2 | * -text 3 | 4 | # Custom for Visual Studio 5 | *.cs -text diff=csharp 6 | *.csproj -text merge=union 7 | *.sln -text merge=union eol=crlf 8 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/DiskAccessLibrary.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | DiskAccessLibrary.Tests 6 | DiskAccessLibrary.Tests 7 | Library 8 | Debug;Release;Publish 9 | Tal Aloni 10 | Copyright © Tal Aloni 2018-2024 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Always 31 | 32 | 33 | Always 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/Helpers/ChkdskHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | 11 | namespace DiskAccessLibrary.Tests 12 | { 13 | public class ChkdskHelper 14 | { 15 | private static readonly string ChkdskExecutablePath = "chkdsk.exe"; 16 | 17 | /// true if no errors were found 18 | public static bool Chkdsk(string driveName) 19 | { 20 | if (driveName.EndsWith("\\")) 21 | { 22 | driveName = driveName.Substring(0, 2); 23 | } 24 | 25 | Process process = Process.Start(ChkdskExecutablePath, driveName); 26 | process.WaitForExit(); 27 | // CHKDSK exit codes: 28 | // 0 - No errors were found. 29 | // 1 - Errors were found and fixed. 30 | // 2 - Performed disk cleanup (such as garbage collection) or did not perform cleanup because /f was not specified. 31 | // 3 - Could not check the disk, errors could not be fixed, or errors were not fixed because /f was not specified. 32 | 33 | // Note: Exit code 2 reported by CHKDSK effectively means that no errors were found. 34 | // I have ran CHKDSK on two identical copies of the same freshly formatted volume and observed it returning 0 in one case and 2 in another after the initial mount. 35 | return (process.ExitCode == 0 || process.ExitCode == 2); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/Helpers/MountHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Threading; 11 | 12 | namespace DiskAccessLibrary.Tests 13 | { 14 | public class MountHelper 15 | { 16 | public static string WaitForDriveToMount(string volumeLabel) 17 | { 18 | return WaitForDriveToMount(volumeLabel, 30); 19 | } 20 | 21 | public static string WaitForDriveToMount(string volumeLabel, int timeout) 22 | { 23 | int count = 0; 24 | while (count < timeout) 25 | { 26 | string driveName = GetDriveByVolumeLabel(volumeLabel); 27 | if (driveName != null) 28 | { 29 | return driveName; 30 | } 31 | Thread.Sleep(1000); 32 | count++; 33 | } 34 | 35 | return null; 36 | } 37 | 38 | public static string GetDriveByVolumeLabel(string volumeLabel) 39 | { 40 | DriveInfo[] drives = DriveInfo.GetDrives(); 41 | foreach (DriveInfo drive in drives) 42 | { 43 | if (drive.DriveType == DriveType.Fixed && drive.VolumeLabel == volumeLabel) 44 | { 45 | return drive.Name; 46 | } 47 | } 48 | 49 | return null; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/Helpers/VHDMountHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Diagnostics; 9 | using System.IO; 10 | 11 | namespace DiskAccessLibrary.Tests 12 | { 13 | public class VHDMountHelper 14 | { 15 | private static readonly string VHDMountExecutablePath = @"C:\Program Files\Microsoft Virtual Server\Vhdmount\vhdmount.exe"; 16 | 17 | public static bool IsVHDMountInstalled() 18 | { 19 | return File.Exists(VHDMountExecutablePath); 20 | } 21 | 22 | public static void MountVHD(string path) 23 | { 24 | if (!IsVHDMountInstalled()) 25 | { 26 | throw new FileNotFoundException("vhdmount.exe was not found, please install Virtual Server 2005 R2 SP1 (select the VHDMount component)"); 27 | } 28 | 29 | string arguments = String.Format("/p /f \"{0}\"", path); 30 | Process process = Process.Start(VHDMountExecutablePath, arguments); 31 | process.WaitForExit(); 32 | if (process.ExitCode != 0) 33 | { 34 | throw new Exception("Failed to mount the VHD"); 35 | } 36 | } 37 | 38 | public static void UnmountVHD(string path) 39 | { 40 | string arguments = String.Format("/u \"{0}\"", path); 41 | int count = 0; 42 | // Sometimes a volume needs some time before it can be dismounted successfully 43 | while (count < 3) 44 | { 45 | Process process = Process.Start(VHDMountExecutablePath, arguments); 46 | process.WaitForExit(); 47 | if (process.ExitCode == 0) 48 | { 49 | return; 50 | } 51 | 52 | count++; 53 | } 54 | 55 | throw new Exception("Failed to unmount the VHD"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/IntegrationTests/FileSystems/NTFS/NTFSFormatTests.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | using DiskAccessLibrary.FileSystems.NTFS; 10 | using Microsoft.VisualStudio.TestTools.UnitTesting; 11 | 12 | namespace DiskAccessLibrary.Tests.IntegrationTests.FileSystems.NTFS 13 | { 14 | [TestClass] 15 | public class NTFSFormatTests 16 | { 17 | private const long DiskSizeInBytes = 100 * 1024 * 1024; 18 | 19 | private VirtualHardDisk m_disk; 20 | 21 | [TestInitialize] 22 | public void Initialize() 23 | { 24 | string diskPath = $@"C:\FormatTest_{Guid.NewGuid()}.vhd"; 25 | m_disk = VirtualHardDisk.CreateFixedDisk(diskPath, DiskSizeInBytes); 26 | } 27 | 28 | [TestCleanup] 29 | public void Cleanup() 30 | { 31 | File.Delete(m_disk.Path); 32 | } 33 | 34 | [DataTestMethod] 35 | [DataRow(512)] 36 | [DataRow(1024)] 37 | [DataRow(2048)] 38 | [DataRow(4096)] 39 | [DataRow(8192)] 40 | [DataRow(16384)] 41 | [DataRow(32768)] 42 | [DataRow(65536)] 43 | public void WhenVolumeIsFormatted_ChkdskReportNoErrors(int bytesPerCluster) 44 | { 45 | Assert.IsTrue(VHDMountHelper.IsVHDMountInstalled(), "vhdmount.exe was not found! Please install Virtual Server 2005 R2 SP1 (select the VHDMount component)"); 46 | 47 | string volumeLabel = "FormatTest_" + bytesPerCluster.ToString(); 48 | m_disk.ExclusiveLock(); 49 | Partition partition = CreatePrimaryPartition(m_disk); 50 | NTFSVolumeCreator.Format(partition, bytesPerCluster, volumeLabel); 51 | m_disk.ReleaseLock(); 52 | VHDMountHelper.MountVHD(m_disk.Path); 53 | string driveName = MountHelper.WaitForDriveToMount(volumeLabel); 54 | if (driveName == null) 55 | { 56 | throw new Exception("Timeout waiting for volume to mount"); 57 | } 58 | bool isErrorFree = ChkdskHelper.Chkdsk(driveName); 59 | if (!isErrorFree) 60 | { 61 | throw new InvalidDataException("CHKDSK reported errors"); 62 | } 63 | VHDMountHelper.UnmountVHD(m_disk.Path); 64 | } 65 | 66 | public static Partition CreatePrimaryPartition(Disk disk) 67 | { 68 | MasterBootRecord mbr = new MasterBootRecord(); 69 | mbr.DiskSignature = (uint)new Random().Next(Int32.MaxValue); 70 | mbr.PartitionTable[0].PartitionTypeName = PartitionTypeName.Primary; 71 | mbr.PartitionTable[0].FirstSectorLBA = 63; 72 | mbr.PartitionTable[0].SectorCountLBA = (uint)Math.Min(disk.TotalSectors - 63, UInt32.MaxValue); 73 | MasterBootRecord.WriteToDisk(disk, mbr); 74 | return BasicDiskHelper.GetPartitions(disk)[0]; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/TestData/VMDK/MonolithicSparse.vmdk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalAloni/DiskAccessLibrary/f32da3743faacf412ac6e540178afaacbd2abe80/DiskAccessLibrary.Tests/TestData/VMDK/MonolithicSparse.vmdk -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/TestData/VMDK/StreamOptimized.vmdk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalAloni/DiskAccessLibrary/f32da3743faacf412ac6e540178afaacbd2abe80/DiskAccessLibrary.Tests/TestData/VMDK/StreamOptimized.vmdk -------------------------------------------------------------------------------- /DiskAccessLibrary.Tests/UnitTests/FileRecordTests.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using DiskAccessLibrary.FileSystems.NTFS; 10 | using Microsoft.VisualStudio.TestTools.UnitTesting; 11 | using Utilities; 12 | 13 | namespace DiskAccessLibrary.Tests.UnitTests 14 | { 15 | [TestClass] 16 | public class FileRecordTests 17 | { 18 | /// 19 | /// This test checks that attributes are deep cloned when assembled from FileRecordSegment list and sliced into FileRecordSegment list. 20 | /// 21 | [TestMethod] 22 | public void AttributeCloneTest() 23 | { 24 | byte minorNTFSVersion = 1; 25 | int bytesPerFileRecordSegment = 1024; 26 | FileRecordSegment baseSegment = new FileRecordSegment(30, 1); 27 | FileRecordSegment segment2 = new FileRecordSegment(31, 1, baseSegment.SegmentReference); 28 | FileNameRecord fileNameRecord = new FileNameRecord(NTFSVolume.RootDirSegmentReference, "john.txt", false, DateTime.Now); 29 | FileNameAttributeRecord fileNameAttribute = new FileNameAttributeRecord(String.Empty); 30 | fileNameAttribute.Record = fileNameRecord; 31 | NonResidentAttributeRecord dataRecord = new NonResidentAttributeRecord(AttributeType.Data, String.Empty); 32 | baseSegment.ImmediateAttributes.Add(fileNameAttribute); 33 | baseSegment.ImmediateAttributes.Add(dataRecord); 34 | dataRecord.DataRunSequence.Add(new DataRun(5, 0)); 35 | byte[] segmentBytesBefore = baseSegment.GetBytes(bytesPerFileRecordSegment, minorNTFSVersion, false); 36 | 37 | List segments = new List(); 38 | segments.Add(baseSegment); 39 | segments.Add(segment2); 40 | FileRecord fileRecord = new FileRecord(segments); 41 | ((NonResidentAttributeRecord)fileRecord.DataRecord).DataRunSequence[0].RunLength = 8; 42 | fileRecord.FileNameRecord.ParentDirectory = new MftSegmentReference(8, 8); 43 | fileRecord.FileNameRecord.FileName = "d"; 44 | byte[] segmentBytesAfter = baseSegment.GetBytes(bytesPerFileRecordSegment, minorNTFSVersion, false); 45 | if (!ByteUtils.AreByteArraysEqual(segmentBytesBefore, segmentBytesAfter)) 46 | { 47 | throw new Exception("Test failed"); 48 | } 49 | 50 | fileRecord.UpdateSegments(1024, 1); 51 | byte[] segmentBytesBefore2 = fileRecord.Segments[0].GetBytes(bytesPerFileRecordSegment, minorNTFSVersion, false); 52 | ((NonResidentAttributeRecord)fileRecord.DataRecord).DataRunSequence[0].RunLength = 10; 53 | fileRecord.FileNameRecord.FileName = "x"; 54 | byte[] segmentBytesAfter2 = fileRecord.Segments[0].GetBytes(bytesPerFileRecordSegment, minorNTFSVersion, false); 55 | if (!ByteUtils.AreByteArraysEqual(segmentBytesBefore2, segmentBytesAfter2)) 56 | { 57 | throw new Exception("Test failed"); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/DiskAccessLibrary.Win32.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net20;net40;netstandard2.0 5 | DiskAccessLibrary.Win32 6 | 1.6.3 7 | 1573;1591 8 | DiskAccessLibrary.Win32 9 | false 10 | Tal Aloni 11 | Copyright © Tal Aloni 2012-2024 12 | DiskAccessLibrary.Win32 extends DiskAccessLibrary with Windows specific capabilities 13 | LGPL-3.0-or-later 14 | https://github.com/TalAloni/DiskAccessLibrary 15 | https://github.com/TalAloni/DiskAccessLibrary 16 | true 17 | Debug;Release;Publish 18 | embedded 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 5.0.0 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/Disks/PhysicalDiskHandlePool.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using Microsoft.Win32.SafeHandles; 10 | 11 | namespace DiskAccessLibrary.Win32 12 | { 13 | public class PhysicalDiskHandlePool 14 | { 15 | // We will use the handle pool to share handles to disks across the application (useful when handle need to lock access to a device) 16 | private static Dictionary m_handlePool = new Dictionary(); 17 | 18 | /// True if a new handle has been allocated for the caller, the caller must release the handle eventually 19 | public static SafeFileHandle ObtainHandle(int physicalDiskIndex, FileAccess access, ShareMode shareMode, out bool newAllocation) 20 | { 21 | if (m_handlePool.ContainsKey(physicalDiskIndex)) 22 | { 23 | newAllocation = false; 24 | return m_handlePool[physicalDiskIndex]; 25 | } 26 | else 27 | { 28 | newAllocation = true; 29 | SafeFileHandle handle = HandleUtils.GetDiskHandle(physicalDiskIndex, access, shareMode); 30 | m_handlePool.Add(physicalDiskIndex, handle); 31 | return handle; 32 | } 33 | } 34 | 35 | public static bool ReleaseHandle(int physicalDiskIndex) 36 | { 37 | if (m_handlePool.ContainsKey(physicalDiskIndex)) 38 | { 39 | SafeFileHandle handle = m_handlePool[physicalDiskIndex]; 40 | if (!handle.IsClosed) 41 | { 42 | handle.Close(); 43 | } 44 | m_handlePool.Remove(physicalDiskIndex); 45 | return true; 46 | } 47 | else 48 | { 49 | return false; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/Enums/LockStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.Win32 3 | { 4 | public enum LockStatus 5 | { 6 | Success, 7 | CannotLockDisk, 8 | CannotLockVolume, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/Helpers/LockHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.Win32 11 | { 12 | public partial class LockHelper 13 | { 14 | /// 15 | /// Will lock physical basic disk and all volumes on it. 16 | /// If the operation is not completed successfully, all locks will be releases. 17 | /// 18 | public static LockStatus LockBasicDiskAndVolumesOrNone(PhysicalDisk disk) 19 | { 20 | bool success = disk.ExclusiveLock(); 21 | if (!success) 22 | { 23 | return LockStatus.CannotLockDisk; 24 | } 25 | List partitions = BasicDiskHelper.GetPartitions(disk); 26 | List volumeGuids = new List(); 27 | foreach (Partition partition in partitions) 28 | { 29 | Guid? windowsVolumeGuid = WindowsVolumeHelper.GetWindowsVolumeGuid(partition); 30 | if (windowsVolumeGuid.HasValue) 31 | { 32 | volumeGuids.Add(windowsVolumeGuid.Value); 33 | } 34 | else 35 | { 36 | return LockStatus.CannotLockVolume; 37 | } 38 | } 39 | 40 | success = LockAllVolumesOrNone(volumeGuids); 41 | if (!success) 42 | { 43 | disk.ReleaseLock(); 44 | return LockStatus.CannotLockVolume; 45 | } 46 | return LockStatus.Success; 47 | } 48 | 49 | public static void UnlockBasicDiskAndVolumes(PhysicalDisk disk) 50 | { 51 | List partitions = BasicDiskHelper.GetPartitions(disk); 52 | foreach (Partition partition in partitions) 53 | { 54 | Guid? windowsVolumeGuid = WindowsVolumeHelper.GetWindowsVolumeGuid(partition); 55 | if (windowsVolumeGuid.HasValue) 56 | { 57 | WindowsVolumeManager.ReleaseLock(windowsVolumeGuid.Value); 58 | } 59 | } 60 | 61 | disk.ReleaseLock(); 62 | } 63 | 64 | public static bool LockAllVolumesOrNone(List volumeGuids) 65 | { 66 | bool success = true; 67 | int lockIndex; 68 | for (lockIndex = 0; lockIndex < volumeGuids.Count; lockIndex++) 69 | { 70 | Guid volumeGuid = volumeGuids[lockIndex]; 71 | if (WindowsVolumeManager.IsMounted(volumeGuid)) 72 | { 73 | success = WindowsVolumeManager.ExclusiveLock(volumeGuid); 74 | if (!success) 75 | { 76 | break; 77 | } 78 | } 79 | } 80 | 81 | if (!success) 82 | { 83 | // release the volumes that were locked 84 | for (int index = 0; index < lockIndex; index++) 85 | { 86 | WindowsVolumeManager.ReleaseLock(volumeGuids[lockIndex]); 87 | } 88 | } 89 | 90 | return success; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/DiskLockHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using DiskAccessLibrary.Win32; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 11 | { 12 | public class DiskLockHelper 13 | { 14 | public static bool LockAllOrNone(List dynamicDisks) 15 | { 16 | List physicalDisks = new List(); 17 | foreach (DynamicDisk dynamicDisk in dynamicDisks) 18 | { 19 | if (dynamicDisk.Disk is PhysicalDisk) 20 | { 21 | physicalDisks.Add((PhysicalDisk)dynamicDisk.Disk); 22 | } 23 | } 24 | return PhysicalDiskHelper.LockAllOrNone(physicalDisks); 25 | } 26 | 27 | public static void ReleaseLock(List dynamicDisks) 28 | { 29 | List physicalDisks = new List(); 30 | foreach (DynamicDisk dynamicDisk in dynamicDisks) 31 | { 32 | if (dynamicDisk.Disk is PhysicalDisk) 33 | { 34 | ((PhysicalDisk)(dynamicDisk.Disk)).ReleaseLock(); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/DiskOfflineHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using DiskAccessLibrary.Win32; 10 | 11 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 12 | { 13 | public class DiskOfflineHelper 14 | { 15 | public static bool IsDiskGroupOnlineAndWritable(Guid diskGroupGuid) 16 | { 17 | List diskGroup = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); 18 | return IsDiskGroupOnlineAndWritable(diskGroup); 19 | } 20 | 21 | public static bool IsDiskGroupOnlineAndWritable(List diskGroup) 22 | { 23 | List physicalDisks = new List(); 24 | foreach (DynamicDisk dynamicDisk in diskGroup) 25 | { 26 | if (dynamicDisk.Disk is PhysicalDisk) 27 | { 28 | physicalDisks.Add((PhysicalDisk)dynamicDisk.Disk); 29 | } 30 | } 31 | 32 | foreach (PhysicalDisk disk in physicalDisks) 33 | { 34 | bool isReadOnly; 35 | bool isOnline = disk.GetOnlineStatus(out isReadOnly); 36 | if (!isOnline || isReadOnly) 37 | { 38 | return false; 39 | } 40 | } 41 | return true; 42 | } 43 | 44 | /// 45 | /// Will not persist across reboots 46 | /// 47 | public static bool OfflineDiskGroup(Guid diskGroupGuid) 48 | { 49 | List disksToOffline = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); 50 | return OfflineAllOrNone(disksToOffline); 51 | } 52 | 53 | public static void OnlineDiskGroup(Guid diskGroupGuid) 54 | { 55 | List disksToOnline = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); 56 | OnlineAll(disksToOnline); 57 | } 58 | 59 | /// 60 | /// Will not persist across reboots 61 | /// 62 | public static bool OfflineAllOrNone(List disksToOffline) 63 | { 64 | List physicalDisks = new List(); 65 | foreach (DynamicDisk dynamicDisk in disksToOffline) 66 | { 67 | if (dynamicDisk.Disk is PhysicalDisk) 68 | { 69 | physicalDisks.Add((PhysicalDisk)dynamicDisk.Disk); 70 | } 71 | } 72 | return PhysicalDiskHelper.OfflineAllOrNone(physicalDisks); 73 | } 74 | 75 | public static void OnlineAll(List disksToOnline) 76 | { 77 | foreach (DynamicDisk disk in disksToOnline) 78 | { 79 | ((PhysicalDisk)disk.Disk).SetOnlineStatus(true); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/LockHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using DiskAccessLibrary.LogicalDiskManager; 10 | using DiskAccessLibrary.LogicalDiskManager.Win32; 11 | 12 | namespace DiskAccessLibrary.Win32 13 | { 14 | public partial class LockHelper 15 | { 16 | public static LockStatus LockAllOrNone(List disksToLock, List volumesToLock) 17 | { 18 | bool success = DiskLockHelper.LockAllOrNone(disksToLock); 19 | if (!success) 20 | { 21 | return LockStatus.CannotLockDisk; 22 | } 23 | 24 | List volumeGuids = DynamicVolumeHelper.GetVolumeGuids(volumesToLock); 25 | success = LockAllVolumesOrNone(volumeGuids); 26 | if (!success) 27 | { 28 | DiskLockHelper.ReleaseLock(disksToLock); 29 | return LockStatus.CannotLockVolume; 30 | } 31 | 32 | return LockStatus.Success; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/LockManager.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using DiskAccessLibrary.Win32; 10 | 11 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 12 | { 13 | public class LockManager 14 | { 15 | private static List m_lockedDisks = new List(); 16 | private static List m_lockedVolumes = new List(); 17 | 18 | public static LockStatus LockDynamicDiskGroup(Guid diskGroupGuid, bool lockAllDynamicVolumes) 19 | { 20 | List disksToLock = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); 21 | return LockDynamicDiskGroup(disksToLock, lockAllDynamicVolumes); 22 | } 23 | 24 | public static LockStatus LockDynamicDiskGroup(List disksToLock, bool lockAllDynamicVolumes) 25 | { 26 | List volumesToLock = new List(); 27 | 28 | if (lockAllDynamicVolumes) 29 | { 30 | volumesToLock = WindowsDynamicVolumeHelper.GetLockableDynamicVolumes(disksToLock); 31 | } 32 | 33 | LockStatus status = LockHelper.LockAllOrNone(disksToLock, volumesToLock); 34 | if (status == LockStatus.Success) 35 | { 36 | m_lockedDisks.AddRange(disksToLock); 37 | m_lockedVolumes.AddRange(volumesToLock); 38 | } 39 | return status; 40 | } 41 | 42 | public static void UnlockAllDisksAndVolumes() 43 | { 44 | DiskLockHelper.ReleaseLock(m_lockedDisks); 45 | 46 | foreach (DynamicVolume volumeToUnlock in m_lockedVolumes) 47 | { 48 | WindowsVolumeManager.ReleaseLock(volumeToUnlock.VolumeGuid); 49 | } 50 | m_lockedDisks.Clear(); 51 | m_lockedVolumes.Clear(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/PhysicalDiskGroupDatabase.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 11 | { 12 | public partial class PhysicalDiskGroupDatabase 13 | { 14 | public static DiskGroupDatabase ReadFromPhysicalDisks(Guid diskGroupGuid) 15 | { 16 | List dynamicDisks = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); 17 | return DiskGroupDatabase.ReadFromDisks(dynamicDisks, diskGroupGuid); 18 | } 19 | 20 | public static List ReadFromPhysicalDisks() 21 | { 22 | List dynamicDisks = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(); 23 | return DiskGroupDatabase.ReadFromDisks(dynamicDisks); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/WindowsDynamicDiskHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using DiskAccessLibrary.Win32; 11 | 12 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 13 | { 14 | public class WindowsDynamicDiskHelper 15 | { 16 | public static List GetPhysicalDynamicDisks() 17 | { 18 | List disks = PhysicalDiskHelper.GetPhysicalDisks(); 19 | List result = new List(); 20 | foreach (PhysicalDisk disk in disks) 21 | { 22 | DynamicDisk dynamicDisk = DynamicDisk.ReadFromDisk(disk); 23 | if (dynamicDisk != null) 24 | { 25 | result.Add(dynamicDisk); 26 | } 27 | } 28 | return result; 29 | } 30 | 31 | public static List GetPhysicalDynamicDisks(Guid diskGroupGuid) 32 | { 33 | List dynamicDisks = GetPhysicalDynamicDisks(); 34 | return DynamicDiskHelper.FindDiskGroup(dynamicDisks, diskGroupGuid); 35 | } 36 | 37 | public static PrivateHeader FindDiskPrivateHeader(Guid diskGuid) 38 | { 39 | DynamicDisk disk = FindDisk(diskGuid); 40 | if (disk != null) 41 | { 42 | return disk.PrivateHeader; 43 | } 44 | return null; 45 | } 46 | 47 | public static DynamicDisk FindDisk(Guid diskGuid) 48 | { 49 | List diskIndexList = PhysicalDiskControl.GetPhysicalDiskIndexList(); 50 | 51 | foreach (int diskIndex in diskIndexList) 52 | { 53 | PhysicalDisk disk; 54 | try 55 | { 56 | disk = new PhysicalDisk(diskIndex); // will throw an exception if disk is not valid 57 | } 58 | catch (DriveNotFoundException) 59 | { 60 | // The disk must have been removed from the system 61 | continue; 62 | } 63 | catch (DeviceNotReadyException) 64 | { 65 | continue; 66 | } 67 | catch (SharingViolationException) // skip this disk, it's probably being used 68 | { 69 | continue; 70 | } 71 | 72 | DynamicDisk dynamicDisk = DynamicDisk.ReadFromDisk(disk); 73 | if (dynamicDisk != null) 74 | { 75 | if (dynamicDisk.DiskGuid == diskGuid) 76 | { 77 | return dynamicDisk; 78 | } 79 | } 80 | } 81 | return null; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/LogicalDiskManager/WindowsDynamicVolumeHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using DiskAccessLibrary.Win32; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager.Win32 11 | { 12 | public class WindowsDynamicVolumeHelper 13 | { 14 | public static List GetDynamicVolumes() 15 | { 16 | List disks = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(); 17 | return DynamicVolumeHelper.GetDynamicVolumes(disks); 18 | } 19 | 20 | public static List GetLockableDynamicVolumes(List dynamicDisks) 21 | { 22 | List result = new List(); 23 | 24 | List disks = new List(); 25 | foreach (DynamicDisk dynamicDisk in dynamicDisks) 26 | { 27 | if (dynamicDisk.Disk is PhysicalDisk) 28 | { 29 | disks.Add(dynamicDisk); 30 | } 31 | } 32 | 33 | List dynamicVolumes = DynamicVolumeHelper.GetDynamicVolumes(disks); 34 | for (int index = 0; index < dynamicVolumes.Count; index++) 35 | { 36 | // non-operational volumes cannot be locked 37 | if (!dynamicVolumes[index].IsOperational) 38 | { 39 | dynamicVolumes.RemoveAt(index); 40 | index--; 41 | } 42 | } 43 | return dynamicVolumes; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DiskAccessLibrary.Win32/Volumes/VolumeHandlePool.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using Microsoft.Win32.SafeHandles; 11 | 12 | namespace DiskAccessLibrary.Win32 13 | { 14 | public class VolumeHandlePool 15 | { 16 | // We will use the handle pool to share handles to volumes across the application (useful when handle need to lock access to a volume) 17 | private static Dictionary m_handlePool = new Dictionary(); 18 | 19 | /// True if a new handle has been allocated for the caller, the caller must release the handle eventually 20 | public static SafeFileHandle ObtainHandle(Guid volumeGuid, FileAccess access, ShareMode shareMode, out bool newAllocation) 21 | { 22 | if (m_handlePool.ContainsKey(volumeGuid)) 23 | { 24 | newAllocation = false; 25 | return m_handlePool[volumeGuid]; 26 | } 27 | else 28 | { 29 | newAllocation = true; 30 | SafeFileHandle handle = HandleUtils.GetVolumeHandle(volumeGuid, access, shareMode); 31 | m_handlePool.Add(volumeGuid, handle); 32 | return handle; 33 | } 34 | } 35 | 36 | public static bool ReleaseHandle(Guid volumeGuid) 37 | { 38 | if (m_handlePool.ContainsKey(volumeGuid)) 39 | { 40 | SafeFileHandle handle = m_handlePool[volumeGuid]; 41 | if (!handle.IsClosed) 42 | { 43 | handle.Close(); 44 | } 45 | m_handlePool.Remove(volumeGuid); 46 | return true; 47 | } 48 | else 49 | { 50 | return false; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DiskAccessLibrary.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiskAccessLibrary", "DiskAccessLibrary\DiskAccessLibrary.csproj", "{000D0367-63A1-475D-982D-67A0B93BABEB}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{6E0F2D1E-6167-4032-BA90-DEE3A99207D0}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiskAccessLibrary.Tests", "DiskAccessLibrary.Tests\DiskAccessLibrary.Tests.csproj", "{0C358FE7-A183-4F61-AF87-1E3B9A296C96}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiskAccessLibrary.Win32", "DiskAccessLibrary.Win32\DiskAccessLibrary.Win32.csproj", "{5D20BF8C-412F-43C5-8438-971CA8C3EA7D}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Publish|Any CPU = Publish|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Publish|Any CPU.ActiveCfg = Publish|Any CPU 24 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Publish|Any CPU.Build.0 = Publish|Any CPU 25 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {000D0367-63A1-475D-982D-67A0B93BABEB}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Publish|Any CPU.ActiveCfg = Release|Any CPU 30 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Publish|Any CPU.Build.0 = Release|Any CPU 31 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Publish|Any CPU.ActiveCfg = Publish|Any CPU 36 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Publish|Any CPU.Build.0 = Publish|Any CPU 37 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {0C358FE7-A183-4F61-AF87-1E3B9A296C96}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Publish|Any CPU.ActiveCfg = Publish|Any CPU 42 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Publish|Any CPU.Build.0 = Publish|Any CPU 43 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {5D20BF8C-412F-43C5-8438-971CA8C3EA7D}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {1F355049-CBFB-4CA7-9392-48A6128D04CF} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /DiskAccessLibrary/BaseClasses/Disk.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary 9 | { 10 | public abstract class Disk 11 | { 12 | /// 13 | /// Sector refers to physical disk sector 14 | /// 15 | public abstract byte[] ReadSectors(long sectorIndex, int sectorCount); 16 | public abstract void WriteSectors(long sectorIndex, byte[] data); 17 | 18 | public byte[] ReadSector(long sectorIndex) 19 | { 20 | return ReadSectors(sectorIndex, 1); 21 | } 22 | 23 | public abstract int BytesPerSector 24 | { 25 | get; 26 | } 27 | 28 | public abstract long Size 29 | { 30 | get; 31 | } 32 | 33 | public virtual bool IsReadOnly 34 | { 35 | get 36 | { 37 | return false; 38 | } 39 | } 40 | 41 | public long TotalSectors 42 | { 43 | get 44 | { 45 | return this.Size / this.BytesPerSector; 46 | } 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /DiskAccessLibrary/BaseClasses/DiskExtent.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DiskExtent 12 | { 13 | private Disk m_disk; 14 | private long m_firstSector; 15 | private long m_size; // In bytes 16 | 17 | public DiskExtent(Disk disk, long firstSector, long size) 18 | { 19 | m_disk = disk; 20 | m_firstSector = firstSector; 21 | m_size = size; 22 | } 23 | 24 | public byte[] ReadSector(long sectorIndex) 25 | { 26 | return ReadSectors(sectorIndex, 1); 27 | } 28 | 29 | public byte[] ReadSectors(long sectorIndex, int sectorCount) 30 | { 31 | CheckBoundaries(sectorIndex, sectorCount); 32 | return m_disk.ReadSectors(m_firstSector + sectorIndex, sectorCount); 33 | } 34 | 35 | public void WriteSectors(long sectorIndex, byte[] data) 36 | { 37 | CheckBoundaries(sectorIndex, data.Length / this.BytesPerSector); 38 | m_disk.WriteSectors(m_firstSector + sectorIndex, data); 39 | } 40 | 41 | public void CheckBoundaries(long sectorIndex, int sectorCount) 42 | { 43 | if (sectorIndex < 0 || sectorIndex + (sectorCount - 1) >= this.TotalSectors) 44 | { 45 | throw new ArgumentOutOfRangeException("Attempted to access data outside of volume"); 46 | } 47 | } 48 | 49 | public int BytesPerSector 50 | { 51 | get 52 | { 53 | return m_disk.BytesPerSector; 54 | } 55 | } 56 | 57 | public long Size 58 | { 59 | get 60 | { 61 | return m_size; 62 | } 63 | } 64 | 65 | public bool IsReadOnly 66 | { 67 | get 68 | { 69 | return m_disk.IsReadOnly; 70 | } 71 | } 72 | 73 | public long FirstSector 74 | { 75 | get 76 | { 77 | return m_firstSector; 78 | } 79 | } 80 | 81 | public long TotalSectors 82 | { 83 | get 84 | { 85 | return this.Size / this.BytesPerSector; 86 | } 87 | } 88 | 89 | public long LastSector 90 | { 91 | get 92 | { 93 | return FirstSector + TotalSectors - 1; 94 | } 95 | } 96 | 97 | public Disk Disk 98 | { 99 | get 100 | { 101 | return m_disk; 102 | } 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /DiskAccessLibrary/BaseClasses/IDiskGeometry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary 9 | { 10 | public interface IDiskGeometry 11 | { 12 | long Cylinders 13 | { 14 | get; 15 | } 16 | 17 | int TracksPerCylinder 18 | { 19 | get; 20 | } 21 | 22 | int SectorsPerTrack 23 | { 24 | get; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DiskAccessLibrary/BaseClasses/Volume.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary 11 | { 12 | public abstract class Volume 13 | { 14 | /// 15 | /// Sector refers to physical disk sector, we can only read complete sectors 16 | /// 17 | public abstract byte[] ReadSectors(long sectorIndex, int sectorCount); 18 | public abstract void WriteSectors(long sectorIndex, byte[] data); 19 | 20 | public byte[] ReadSector(long sectorIndex) 21 | { 22 | return ReadSectors(sectorIndex, 1); 23 | } 24 | 25 | public void CheckBoundaries(long sectorIndex, int sectorCount) 26 | { 27 | if (sectorIndex < 0 || sectorIndex + sectorCount - 1 >= this.TotalSectors) 28 | { 29 | throw new ArgumentOutOfRangeException("Attempted to access data outside of volume"); 30 | } 31 | } 32 | 33 | public abstract int BytesPerSector 34 | { 35 | get; 36 | } 37 | 38 | public abstract long Size 39 | { 40 | get; 41 | } 42 | 43 | public virtual bool IsReadOnly 44 | { 45 | get 46 | { 47 | return false; 48 | } 49 | } 50 | 51 | public abstract List Extents 52 | { 53 | get; 54 | } 55 | 56 | public long TotalSectors 57 | { 58 | get 59 | { 60 | return this.Size / this.BytesPerSector; 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /DiskAccessLibrary/DiskAccessLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net20;net40;net472;netstandard2.0 5 | DiskAccessLibrary 6 | 1.6.4.3 7 | 1573;1591 8 | DiskAccessLibrary 9 | false 10 | Tal Aloni 11 | Copyright © Tal Aloni 2012-2025 12 | DiskAccessLibrary is an open-source C# library allowing access to physical and virtual disks (IMG/VHD/VMDK) including reading and writing various on-disk structutes (MBR/GPT, Logical Disk Manager Database) and filesystems (NTFS) 13 | LGPL-3.0-or-later 14 | https://github.com/TalAloni/DiskAccessLibrary 15 | https://github.com/TalAloni/DiskAccessLibrary 16 | true 17 | Debug;Release;Publish 18 | embedded 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | <_Parameter1>DiskAccessLibrary.Tests 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/DiskImage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public abstract partial class DiskImage : Disk 12 | { 13 | private string m_path; 14 | private bool m_isReadOnly; 15 | 16 | public DiskImage(string diskImagePath) : this(diskImagePath, false) 17 | { 18 | } 19 | 20 | public DiskImage(string diskImagePath, bool isReadOnly) 21 | { 22 | m_path = diskImagePath; 23 | m_isReadOnly = isReadOnly; 24 | } 25 | 26 | public void CheckBoundaries(long sectorIndex, int sectorCount) 27 | { 28 | if (sectorIndex < 0 || sectorIndex + (sectorCount - 1) >= this.TotalSectors) 29 | { 30 | throw new ArgumentOutOfRangeException("Attempted to access data outside of disk"); 31 | } 32 | } 33 | 34 | public abstract void Extend(long numberOfAdditionalBytes); 35 | 36 | public abstract bool ExclusiveLock(); 37 | 38 | public abstract bool ExclusiveLock(bool useOverlappedIO); 39 | 40 | public abstract bool ReleaseLock(); 41 | 42 | public string Path 43 | { 44 | get 45 | { 46 | return m_path; 47 | } 48 | } 49 | 50 | public override bool IsReadOnly 51 | { 52 | get 53 | { 54 | return m_isReadOnly; 55 | } 56 | } 57 | 58 | /// 59 | /// 60 | /// 61 | /// 62 | public static DiskImage GetDiskImage(string path) 63 | { 64 | return GetDiskImage(path, false); 65 | } 66 | 67 | /// 68 | /// 69 | /// 70 | /// 71 | public static DiskImage GetDiskImage(string path, bool isReadOnly) 72 | { 73 | if (path.EndsWith(".vhd", StringComparison.InvariantCultureIgnoreCase)) 74 | { 75 | return new VirtualHardDisk(path, isReadOnly); 76 | } 77 | else if (path.EndsWith(".vmdk", StringComparison.InvariantCultureIgnoreCase)) 78 | { 79 | return new VirtualMachineDisk(path, isReadOnly); 80 | } 81 | else 82 | { 83 | return new RawDiskImage(path, isReadOnly); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/RAMDisk.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using Utilities; 9 | 10 | namespace DiskAccessLibrary 11 | { 12 | public class RAMDisk : Disk 13 | { 14 | public const int BytesPerRAMDiskSector = 512; 15 | 16 | private byte[] m_diskBytes; 17 | 18 | /// 19 | /// A single-dimensional byte array cannot contain more than 0X7FFFFFC7 bytes (2047.999 MiB). 20 | /// https://msdn.microsoft.com/en-us/library/System.Array(v=vs.110).aspx 21 | /// 22 | public RAMDisk(int size) 23 | { 24 | m_diskBytes = new byte[size]; 25 | } 26 | 27 | public void Free() 28 | { 29 | m_diskBytes = null; 30 | GC.Collect(); 31 | GC.WaitForPendingFinalizers(); 32 | } 33 | 34 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 35 | { 36 | return ByteReader.ReadBytes(m_diskBytes, (int)sectorIndex * BytesPerRAMDiskSector, sectorCount * BytesPerRAMDiskSector); 37 | } 38 | 39 | public override void WriteSectors(long sectorIndex, byte[] data) 40 | { 41 | ByteWriter.WriteBytes(m_diskBytes, (int)sectorIndex * BytesPerRAMDiskSector, data); 42 | } 43 | 44 | public override int BytesPerSector 45 | { 46 | get 47 | { 48 | return BytesPerRAMDiskSector; 49 | } 50 | } 51 | 52 | public override long Size 53 | { 54 | get 55 | { 56 | return m_diskBytes.Length; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VHD/BlockAllocationTable.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using Utilities; 9 | 10 | namespace DiskAccessLibrary.VHD 11 | { 12 | /// 13 | /// a.k.a. BAT 14 | /// 15 | public class BlockAllocationTable 16 | { 17 | public const uint UnusedEntry = 0xFFFFFFFF; 18 | private uint[] m_entries; 19 | 20 | public BlockAllocationTable(uint maxTableEntries) 21 | { 22 | m_entries = new uint[maxTableEntries]; 23 | for (int index = 0; index < maxTableEntries; index++) 24 | { 25 | m_entries[index] = UnusedEntry; 26 | } 27 | } 28 | 29 | public BlockAllocationTable(byte[] buffer, uint maxTableEntries) 30 | { 31 | m_entries = new uint[maxTableEntries]; 32 | for (int index = 0; index < maxTableEntries; index++) 33 | { 34 | m_entries[index] = BigEndianConverter.ToUInt32(buffer, index * 4); 35 | } 36 | } 37 | 38 | public byte[] GetBytes() 39 | { 40 | // The BAT is always extended to a sector boundary 41 | int bufferLength = (int)Math.Ceiling((double)m_entries.Length * 4 / VirtualHardDisk.BytesPerDiskSector) * VirtualHardDisk.BytesPerDiskSector; 42 | byte[] buffer = new byte[bufferLength]; 43 | for (int index = 0; index < m_entries.Length; index++) 44 | { 45 | BigEndianWriter.WriteUInt32(buffer, index * 4, m_entries[index]); 46 | } 47 | 48 | return buffer; 49 | } 50 | 51 | public bool IsBlockInUse(uint blockIndex) 52 | { 53 | return m_entries[blockIndex] != UnusedEntry; 54 | } 55 | 56 | public bool IsBlockInUse(uint blockIndex, out uint blockStartSector) 57 | { 58 | blockStartSector = m_entries[blockIndex]; 59 | return m_entries[blockIndex] != UnusedEntry; 60 | } 61 | 62 | public void SetBlockStartSector(uint blockIndex, uint blockStartSector) 63 | { 64 | if (m_entries[blockIndex] != UnusedEntry) 65 | { 66 | throw new InvalidOperationException("Block is already allocated"); 67 | } 68 | 69 | m_entries[blockIndex] = blockStartSector; 70 | } 71 | 72 | public static BlockAllocationTable ReadBlockAllocationTable(string path, DynamicDiskHeader dynamicHeader) 73 | { 74 | uint maxTableEntries = dynamicHeader.MaxTableEntries; 75 | long sectorIndex = (long)(dynamicHeader.TableOffset / VirtualHardDisk.BytesPerDiskSector); 76 | int sectorCount = (int)Math.Ceiling((double)maxTableEntries * 4 / VirtualHardDisk.BytesPerDiskSector); 77 | byte[] buffer = new RawDiskImage(path, VirtualHardDisk.BytesPerDiskSector).ReadSectors(sectorIndex, sectorCount); 78 | return new BlockAllocationTable(buffer, maxTableEntries); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VHD/ParentLocatorEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.VHD 10 | { 11 | public class ParentLocatorEntry 12 | { 13 | public const int Length = 24; 14 | 15 | public uint PlatformCode; 16 | public uint PlatformDataSpace; 17 | public uint PlatformDataLength; 18 | public uint Reserved; 19 | public ulong PlatformDataOffset; 20 | 21 | public ParentLocatorEntry() 22 | { 23 | } 24 | 25 | public ParentLocatorEntry(byte[] buffer, int offset) 26 | { 27 | PlatformCode = BigEndianConverter.ToUInt32(buffer, offset + 0); 28 | PlatformDataSpace = BigEndianConverter.ToUInt32(buffer, offset + 4); 29 | PlatformDataLength = BigEndianConverter.ToUInt32(buffer, offset + 8); 30 | Reserved = BigEndianConverter.ToUInt32(buffer, offset + 12); 31 | PlatformDataOffset = BigEndianConverter.ToUInt64(buffer, offset + 16); 32 | } 33 | 34 | public void WriteBytes(byte[] buffer, int offset) 35 | { 36 | BigEndianWriter.WriteUInt32(buffer, offset + 0, PlatformCode); 37 | BigEndianWriter.WriteUInt32(buffer, offset + 4, PlatformDataSpace); 38 | BigEndianWriter.WriteUInt32(buffer, offset + 8, PlatformDataLength); 39 | BigEndianWriter.WriteUInt32(buffer, offset + 12, Reserved); 40 | BigEndianWriter.WriteUInt64(buffer, offset + 16, PlatformDataOffset); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VHD/VirtualHardDiskType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.VHD 3 | { 4 | public enum VirtualHardDiskType 5 | { 6 | Fixed = 2, 7 | Dynamic = 3, 8 | Differencing = 4, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VMDK/Enums/ExtentType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.VMDK 3 | { 4 | public enum ExtentType 5 | { 6 | Flat, 7 | Sparse, 8 | Zero, 9 | VMFS, 10 | VMFSSparse, 11 | VMFSRDM, 12 | VMFSRaw, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VMDK/Enums/SparseExtentCompression.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.VMDK 3 | { 4 | public enum SparseExtentCompression : ushort 5 | { 6 | None = 0, // COMPRESSION_NONE 7 | Deflate = 1, // COMPRESSION_DEFLATE 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VMDK/Enums/SparseExtentHeaderFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.VMDK 4 | { 5 | [Flags] 6 | public enum SparseExtentHeaderFlags : uint 7 | { 8 | ValidNewLineDetectionTest = 0x00000001, 9 | HasRedundantGrainTable = 0x00000002, 10 | UseZeroedGrainGTEs = 0x00000004, // SparseExtentHeader version 2 and above, see Virtual Disk Format 5.0 11 | UseCompressionForGrains = 0x00010000, 12 | HasMarkers = 0x00020000, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Disks/VMDK/Enums/VirtualMachineDiskType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.VMDK 3 | { 4 | public enum VirtualMachineDiskType 5 | { 6 | Custom, 7 | MonolithicSparse, // Single sparse extent with embedded descriptor file 8 | MonolithicFlat, // Single flat extent with separate descriptor file 9 | TwoGbMaxExtentSparse, 10 | TwoGbMaxExtentFlat, 11 | FullDevice, 12 | PartitionedDevice, 13 | VmfsPreallocated, 14 | VmfsEagerZeroedThick, 15 | VmfsThin, 16 | VmfsSparse, 17 | VmfsRDM, 18 | VmfsRDMP, 19 | VmfsRaw, 20 | StreamOptimized, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/AlreadyExistsException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class AlreadyExistsException : IOException 12 | { 13 | public AlreadyExistsException() : this("Already Exists") 14 | { 15 | } 16 | 17 | public AlreadyExistsException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_ALREADY_EXISTS); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/CyclicRedundancyCheckException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class CyclicRedundancyCheckException : IOException 12 | { 13 | public CyclicRedundancyCheckException() : this("Data Error (Cyclic Redundancy Check)") 14 | { 15 | } 16 | 17 | public CyclicRedundancyCheckException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_CRC); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/DeviceNotReadyException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DeviceNotReadyException : IOException 12 | { 13 | public DeviceNotReadyException() : this("Device Not Ready") 14 | { 15 | } 16 | 17 | public DeviceNotReadyException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_NOT_READY); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/DirectoryNotEmptyException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DirectoryNotEmptyException : IOException 12 | { 13 | public DirectoryNotEmptyException() : this("The directory is not empty") 14 | { 15 | } 16 | 17 | public DirectoryNotEmptyException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_DIR_NOT_EMPTY); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/DiskFullException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DiskFullException : IOException 12 | { 13 | public DiskFullException() : this("Disk Full") 14 | { 15 | } 16 | 17 | public DiskFullException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_DISK_FULL); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/InvalidNameException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class InvalidNameException : IOException 12 | { 13 | public InvalidNameException() : this("The filename, directory name, or volume label syntax is incorrect") 14 | { 15 | } 16 | 17 | public InvalidNameException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_INVALID_NAME); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/InvalidPathException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class InvalidPathException : IOException 12 | { 13 | public InvalidPathException() : this("Invalid path") 14 | { 15 | } 16 | 17 | public InvalidPathException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_BAD_PATHNAME); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Exceptions/SharingViolationException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class SharingViolationException : IOException 12 | { 13 | public SharingViolationException() : this("Sharing Violation") 14 | { 15 | } 16 | 17 | public SharingViolationException(string message) : base(message) 18 | { 19 | HResult = IOExceptionHelper.GetHResultFromWin32Error(Win32Error.ERROR_SHARING_VIOLATION); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/Abstractions/FileSystemEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary.FileSystems.Abstractions 10 | { 11 | public class FileSystemEntry 12 | { 13 | /// 14 | /// Full Path. Directory path should end with a trailing slash. 15 | /// 16 | public string FullName; 17 | public string Name; 18 | public bool IsDirectory; 19 | public ulong Size; 20 | public DateTime CreationTime; 21 | public DateTime LastWriteTime; 22 | public DateTime LastAccessTime; 23 | public bool IsHidden; 24 | public bool IsReadonly; 25 | public bool IsArchived; 26 | 27 | public FileSystemEntry(string fullName, string name, bool isDirectory, ulong size, DateTime creationTime, DateTime lastWriteTime, DateTime lastAccessTime, bool isHidden, bool isReadonly, bool isArchived) 28 | { 29 | FullName = fullName; 30 | Name = name; 31 | IsDirectory = isDirectory; 32 | Size = size; 33 | CreationTime = creationTime; 34 | LastWriteTime = lastWriteTime; 35 | LastAccessTime = lastAccessTime; 36 | IsHidden = isHidden; 37 | IsReadonly = isReadonly; 38 | IsArchived = isArchived; 39 | 40 | if (isDirectory) 41 | { 42 | FullName = FileSystem.GetDirectoryPath(FullName); 43 | } 44 | } 45 | 46 | public FileSystemEntry Clone() 47 | { 48 | FileSystemEntry clone = (FileSystemEntry)MemberwiseClone(); 49 | return clone; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/FileSystemHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | using DiskAccessLibrary.FileSystems.Abstractions; 10 | using DiskAccessLibrary.FileSystems.NTFS; 11 | 12 | namespace DiskAccessLibrary.FileSystems 13 | { 14 | public class FileSystemHelper 15 | { 16 | public static FileSystem ReadFileSystem(Volume volume) 17 | { 18 | return ReadFileSystem(volume, false); 19 | } 20 | 21 | public static FileSystem ReadFileSystem(Volume volume, bool isReadOnly) 22 | { 23 | try 24 | { 25 | return new NTFSFileSystem(volume, isReadOnly); 26 | } 27 | catch (InvalidDataException) 28 | { 29 | } 30 | catch (NotSupportedException) 31 | { 32 | } 33 | 34 | return null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/IExtendableFileSystem.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary.FileSystems 9 | { 10 | public interface IExtendableFileSystem 11 | { 12 | /// In bytes 13 | long GetMaximumSizeToExtend(); 14 | void Extend(long numberOfAdditionalSectors); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/AttributeDefinition.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using Utilities; 10 | 11 | namespace DiskAccessLibrary.FileSystems.NTFS 12 | { 13 | public class AttributeDefinition : NTFSFile 14 | { 15 | private List m_list; 16 | 17 | public AttributeDefinition(NTFSVolume volume) : base(volume, MasterFileTable.AttrDefSegmentReference) 18 | { 19 | } 20 | 21 | public List ReadList() 22 | { 23 | ulong position = 0; 24 | List entries = new List(); 25 | while (position < this.Data.Length) 26 | { 27 | byte[] entryBytes = this.ReadData(position, AttributeDefinitionEntry.Length); 28 | entries.Add(new AttributeDefinitionEntry(entryBytes, 0)); 29 | position += AttributeDefinitionEntry.Length; 30 | } 31 | return entries; 32 | } 33 | 34 | public List List 35 | { 36 | get 37 | { 38 | if (m_list == null) 39 | { 40 | m_list = ReadList(); 41 | } 42 | return m_list; 43 | } 44 | } 45 | 46 | public static byte[] GetBytes(List entries) 47 | { 48 | int length = entries.Count * AttributeDefinitionEntry.Length; 49 | byte[] buffer = new byte[length]; 50 | int position = 0; 51 | foreach (AttributeDefinitionEntry entry in entries) 52 | { 53 | entry.WriteBytes(buffer, position); 54 | position += AttributeDefinitionEntry.Length; 55 | } 56 | return buffer; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/FileNameAttributeRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary.FileSystems.NTFS 9 | { 10 | /// 11 | /// FileName attribute is always resident. 12 | /// 13 | public class FileNameAttributeRecord : ResidentAttributeRecord 14 | { 15 | public FileNameRecord Record; 16 | 17 | public FileNameAttributeRecord(string name) : base(AttributeType.FileName, name) 18 | { 19 | } 20 | 21 | public FileNameAttributeRecord(byte[] buffer, int offset) : base(buffer, offset) 22 | { 23 | Record = new FileNameRecord(this.Data, 0); 24 | } 25 | 26 | public override byte[] GetBytes() 27 | { 28 | this.Data = Record.GetBytes(); 29 | 30 | return base.GetBytes(); 31 | } 32 | 33 | public override AttributeRecord Clone() 34 | { 35 | FileNameAttributeRecord clone = (FileNameAttributeRecord)base.Clone(); 36 | clone.Record = this.Record.Clone(); 37 | return clone; 38 | } 39 | 40 | public override ulong DataLength 41 | { 42 | get 43 | { 44 | return (ulong)Record.Length; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/IndexAllocationRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary.FileSystems.NTFS 9 | { 10 | /// 11 | /// IndexAllocation attribute is always non-resident. 12 | /// 13 | public class IndexAllocationRecord : NonResidentAttributeRecord 14 | { 15 | public IndexAllocationRecord(string name) : base(AttributeType.IndexAllocation, name) 16 | { 17 | } 18 | 19 | public IndexAllocationRecord(byte[] buffer, int offset) : base(buffer, offset) 20 | { 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/VolumeInformationRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// VolumeInformation attribute is always resident. 13 | /// 14 | public class VolumeInformationRecord : ResidentAttributeRecord 15 | { 16 | public const int RecordDataLength = 12; 17 | 18 | // ulong Reserved; 19 | public byte MajorVersion; 20 | public byte MinorVersion; 21 | public VolumeFlags VolumeFlags; 22 | 23 | public VolumeInformationRecord(string name) : base(AttributeType.VolumeInformation, name) 24 | { 25 | MajorVersion = 3; 26 | MinorVersion = 1; 27 | } 28 | 29 | public VolumeInformationRecord(byte[] buffer, int offset) : base(buffer, offset) 30 | { 31 | MajorVersion = ByteReader.ReadByte(this.Data, 0x08); 32 | MinorVersion = ByteReader.ReadByte(this.Data, 0x09); 33 | VolumeFlags = (VolumeFlags)LittleEndianConverter.ToUInt16(this.Data, 0x0A); 34 | } 35 | 36 | public override byte[] GetBytes() 37 | { 38 | this.Data = new byte[this.DataLength]; 39 | ByteWriter.WriteByte(this.Data, 0x08, MajorVersion); 40 | ByteWriter.WriteByte(this.Data, 0x09, MinorVersion); 41 | LittleEndianWriter.WriteUInt16(this.Data, 0x0A, (ushort)VolumeFlags); 42 | 43 | return base.GetBytes(); 44 | } 45 | 46 | public override ulong DataLength 47 | { 48 | get 49 | { 50 | return RecordDataLength; 51 | } 52 | } 53 | 54 | public bool IsDirty 55 | { 56 | get 57 | { 58 | return (VolumeFlags & VolumeFlags.Dirty) != 0; 59 | } 60 | set 61 | { 62 | if (value) 63 | { 64 | VolumeFlags |= VolumeFlags.Dirty; 65 | } 66 | else 67 | { 68 | VolumeFlags &= ~VolumeFlags.Dirty; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/AttributeRecord/VolumeNameRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Text; 9 | 10 | namespace DiskAccessLibrary.FileSystems.NTFS 11 | { 12 | /// 13 | /// VolumeName attribute is always resident. 14 | /// 15 | public class VolumeNameRecord : ResidentAttributeRecord 16 | { 17 | public const int MaxVolumeNameLength = 32; 18 | 19 | private string m_volumeName; 20 | 21 | public VolumeNameRecord(string name) : base(AttributeType.VolumeName, name) 22 | { 23 | m_volumeName = String.Empty; 24 | } 25 | 26 | public VolumeNameRecord(byte[] buffer, int offset) : base(buffer, offset) 27 | { 28 | m_volumeName = Encoding.Unicode.GetString(this.Data); 29 | } 30 | 31 | public override byte[] GetBytes() 32 | { 33 | this.Data = Encoding.Unicode.GetBytes(m_volumeName); 34 | 35 | return base.GetBytes(); 36 | } 37 | 38 | public override ulong DataLength 39 | { 40 | get 41 | { 42 | return (ulong)m_volumeName.Length * 2; 43 | } 44 | } 45 | 46 | public string VolumeName 47 | { 48 | get 49 | { 50 | return m_volumeName; 51 | } 52 | set 53 | { 54 | if (value.Length > MaxVolumeNameLength) 55 | { 56 | throw new ArgumentException("Volume name length is limited to 32 characters"); 57 | } 58 | m_volumeName = value; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/AttributeDefinitionFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems 4 | { 5 | [Flags] 6 | public enum AttributeDefinitionFlags : uint 7 | { 8 | Indexable = 0x00000002, // ATTRIBUTE_DEF_INDEXABLE 9 | DuplicatesAllowed = 0x00000004, // ATTRIBUTE_DEF_DUPLICATES_ALLOWED 10 | MayNotBeNull = 0x00000008, // ATTRIBUTE_DEF_MAY_NOT_BE_NULL 11 | MustBeIndexed = 0x00000010, // ATTRIBUTE_DEF_MUST_BE_INDEXED 12 | MustBeNamed = 0x00000020, // ATTRIBUTE_DEF_MUST_BE_NAMED 13 | MustBeResident = 0x00000040, // ATTRIBUTE_DEF_MUST_BE_RESIDENT 14 | LogNonResident = 0x00000080, // ATTRIBUTE_DEF_LOG_NONRESIDENT 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/AttributeFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | /// 6 | /// https://docs.microsoft.com/en-us/windows/desktop/DevNotes/attribute-record-header 7 | /// 8 | [Flags] 9 | public enum AttributeFlags : ushort 10 | { 11 | CompressedUsingLZNT1 = 0x0001, // COMPRESSION_FORMAT_LZNT1 - 1 12 | CompressionMask = 0x00FF, // ATTRIBUTE_FLAG_COMPRESSION_MASK 13 | Encrypted = 0x4000, // ATTRIBUTE_FLAG_ENCRYPTED 14 | Sparse = 0x8000, // ATTRIBUTE_FLAG_SPARSE 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/AttributeForm.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | /// 5 | /// https://docs.microsoft.com/en-us/windows/desktop/DevNotes/attribute-record-header 6 | /// 7 | public enum AttributeForm : byte 8 | { 9 | Resident = 0x00, // RESIDENT_FORM 10 | NonResident = 0x01, // NONRESIDENT_FORM 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/CollationRule.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | public enum CollationRule : uint 5 | { 6 | Binary = 0x00000000, // COLLATION_BINARY 7 | Filename = 0x00000001, // COLLATION_FILE_NAME 8 | UnicodeString = 0x00000002, // COLLATION_UNICODE_STRING 9 | UnsignedLong = 0x00000010, 10 | Sid = 0x00000011, 11 | SecurityHash = 0x00000012, 12 | MultipleUnsignedLongs = 0x00000013, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/ContentType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | public enum ContentType 5 | { 6 | FileData, 7 | IndexBitmap, 8 | IndexData, 9 | LogFileData, 10 | MftBitmap, 11 | MftData, 12 | VolumeBitmap, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/FileAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum FileAttributes : uint 7 | { 8 | Readonly = 0x00000001, // FILE_ATTRIBUTE_READONLY 9 | Hidden = 0x00000002, // FILE_ATTRIBUTE_HIDDEN 10 | System = 0x00000004, // FILE_ATTRIBUTE_SYSTEM 11 | 12 | /// 13 | /// This flag is not in use. 14 | /// FileRecordFlags.IsDirectory is used instead for FileRecordSegment, 15 | /// DUP_FILE_NAME_INDEX_PRESENT is used instead for FileNameRecord. 16 | /// 17 | Directory = 0x00000010, // FILE_ATTRIBUTE_DIRECTORY 18 | Archive = 0x00000020, // FILE_ATTRIBUTE_ARCHIVE 19 | 20 | /// This flag is not in use. 21 | Normal = 0x00000080, // FILE_ATTRIBUTE_NORMAL 22 | Temporary = 0x00000100, // FILE_ATTRIBUTE_TEMPORARY 23 | Sparse = 0x00000200, // FILE_ATTRIBUTE_SPARSE_FILE 24 | ReparsePoint = 0x00000400, // FILE_ATTRIBUTE_REPARSE_POINT 25 | Compressed = 0x00000800, // FILE_ATTRIBUTE_COMPRESSED 26 | Offline = 0x00001000, // FILE_ATTRIBUTE_OFFLINE 27 | PropertySet = 0x00002000, // FILE_ATTRIBUTE_PROPERTY_SET 28 | 29 | /// This flag should only be used in FileNameRecord, and should not be used in StandardInformationRecord 30 | FileNameIndexPresent = 0x10000000, // DUP_FILE_NAME_INDEX_PRESENT 31 | 32 | /// Indicates the presence of object ID index, quota index, security index or EFS related index 33 | /// NTFS 3.0+ 34 | ViewIndexPresent = 0x20000000, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/FileNameFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum FileNameFlags : byte 7 | { 8 | POSIX = 0x00, // 255 Unicode characters 9 | Win32 = 0x01, // FILE_NAME_NTFS, 255 Unicode characters 10 | DOS = 0x02, // FILE_NAME_DOS, 8.3 filename 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/FileRecordFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | /// 6 | /// FILE_RECORD_SEGMENT_HEADER: https://msdn.microsoft.com/de-de/windows/desktop/bb470124 7 | /// 8 | [Flags] 9 | public enum FileRecordFlags : ushort 10 | { 11 | InUse = 0x0001, // FILE_RECORD_SEGMENT_IN_USE 12 | IsDirectory = 0x0002, // FILE_FILE_NAME_INDEX_PRESENT 13 | IsExtension = 0x0004, 14 | IsSpecialIndex = 0x0008, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/IndexEntryFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum IndexEntryFlags : ushort 7 | { 8 | ParentNodeForm = 0x01, // INDEX_ENTRY_NODE 9 | LastEntryInNode = 0x02, // INDEX_ENTRY_END 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/IndexHeaderFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum IndexHeaderFlags : byte 7 | { 8 | /// 9 | /// This node is a parent node (not a leaf) 10 | /// 11 | ParentNode = 0x01, // INDEX_NODE 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/ResidentForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum ResidentForm : byte 7 | { 8 | Indexed = 0x01, // RESIDENT_FORM_INDEXED 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Enums/VolumeFlags.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | public enum VolumeFlags : ushort 5 | { 6 | Dirty = 0x0001, // VOLUME_DIRTY 7 | ResizeLogFile = 0x0002, // VOLUME_RESIZE_LOG_FILE 8 | Repaired = 0x8000, // CHKDSK sets this flag 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/FileRecord/MultiSectorHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.IO; 8 | using Utilities; 9 | 10 | namespace DiskAccessLibrary.FileSystems.NTFS 11 | { 12 | public class MultiSectorHelper 13 | { 14 | public const int BytesPerStride = 512; 15 | 16 | /// 17 | /// The USN will be written at the end of each 512-byte stride, even if the device has more (or less) than 512 bytes per sector. 18 | /// https://docs.microsoft.com/en-us/windows/desktop/DevNotes/multi-sector-header 19 | /// 20 | public static void RevertUsaProtection(byte[] buffer, int offset) 21 | { 22 | MultiSectorHeader multiSectorHeader = new MultiSectorHeader(buffer, offset + 0x00); 23 | int position = offset + multiSectorHeader.UpdateSequenceArrayOffset; 24 | if (position > buffer.Length - 2) 25 | { 26 | throw new InvalidDataException("UpdateSequenceArrayOffset is out of range"); 27 | } 28 | uint updateSequenceNumber = LittleEndianReader.ReadUInt16(buffer, ref position); 29 | 30 | // First do validation check - make sure the USN matches on all sectors) 31 | for (int index = 0; index < multiSectorHeader.UpdateSequenceArraySize - 1; ++index) 32 | { 33 | if (updateSequenceNumber != LittleEndianConverter.ToUInt16(buffer, offset + (BytesPerStride * (index + 1)) - 2)) 34 | { 35 | throw new InvalidDataException("Corrupt multi-sector transfer, USN does not match MultiSectorHeader"); 36 | } 37 | } 38 | 39 | for (int index = 0; index < multiSectorHeader.UpdateSequenceArraySize - 1; index++) 40 | { 41 | byte[] endOfSectorBytes = ByteReader.ReadBytes(buffer, ref position, 2); 42 | ByteWriter.WriteBytes(buffer, offset + (BytesPerStride * (index + 1)) - 2, endOfSectorBytes); 43 | } 44 | } 45 | 46 | public static void ApplyUsaProtection(byte[] buffer, int offset) 47 | { 48 | MultiSectorHeader multiSectorHeader = new MultiSectorHeader(buffer, offset + 0x00); 49 | int position = offset + multiSectorHeader.UpdateSequenceArrayOffset; 50 | ushort updateSequenceNumber = LittleEndianReader.ReadUInt16(buffer, ref position); 51 | 52 | for (int index = 0; index < multiSectorHeader.UpdateSequenceArraySize - 1; index++) 53 | { 54 | // Read in the bytes that are replaced by the USN 55 | byte[] endOfSectorBytes = ByteReader.ReadBytes(buffer, offset + (BytesPerStride * (index + 1)) - 2, 2); 56 | // Relocate the bytes that are replaced by the USN 57 | ByteWriter.WriteBytes(buffer, ref position, endOfSectorBytes); 58 | // Write the USN 59 | LittleEndianWriter.WriteUInt16(buffer, offset + (BytesPerStride * (index + 1)) - 2, updateSequenceNumber); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Index/IndexHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | public class IndexHeader 12 | { 13 | public const int Length = 16; 14 | 15 | public uint EntriesOffset; // Relative to Index Header start offset 16 | public uint TotalLength; // Length from the start of the header to the end of the last entry, a.k.a. FirstFreeByte 17 | public uint AllocatedLength; // BytesAvailable 18 | public IndexHeaderFlags IndexFlags; 19 | // 3 zero bytes 20 | 21 | public IndexHeader() 22 | { 23 | } 24 | 25 | public IndexHeader(byte[] buffer, int offset) 26 | { 27 | EntriesOffset = LittleEndianConverter.ToUInt32(buffer, offset + 0x00); 28 | TotalLength = LittleEndianConverter.ToUInt32(buffer, offset + 0x04); 29 | AllocatedLength = LittleEndianConverter.ToUInt32(buffer, offset + 0x08); 30 | IndexFlags = (IndexHeaderFlags)ByteReader.ReadByte(buffer, offset + 0x0C); 31 | // 3 zero bytes (padding to 8-byte boundary) 32 | } 33 | 34 | public void WriteBytes(byte[] buffer, int offset) 35 | { 36 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x00, EntriesOffset); 37 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x04, TotalLength); 38 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x08, AllocatedLength); 39 | ByteWriter.WriteByte(buffer, offset + 0x0C, (byte)IndexFlags); 40 | } 41 | 42 | public IndexHeader Clone() 43 | { 44 | return (IndexHeader)this.MemberwiseClone(); 45 | } 46 | 47 | public bool IsParentNode 48 | { 49 | get 50 | { 51 | return (IndexFlags & IndexHeaderFlags.ParentNode) > 0; 52 | } 53 | set 54 | { 55 | if (value) 56 | { 57 | IndexFlags |= IndexHeaderFlags.ParentNode; 58 | } 59 | else 60 | { 61 | IndexFlags &= ~IndexHeaderFlags.ParentNode; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/LfsRecordFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum LfsRecordFlags : ushort 7 | { 8 | MultiPage = 0x0001, // LOG_RECORD_MULTI_PAGE 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/LfsRecordPageFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum LfsRecordPageFlags : uint 7 | { 8 | /// Indicates that a log record ends on this page 9 | RecordEnd = 0x00000001, // LOG_PAGE_LOG_RECORD_END 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/LfsRecordType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | public enum LfsRecordType : uint 5 | { 6 | ClientRecord = 1, // LfsClientRecord 7 | ClientRestart = 2, // LfsClientRestart 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/LfsRestartFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.FileSystems.NTFS 4 | { 5 | [Flags] 6 | public enum LfsRestartFlags : ushort 7 | { 8 | /// Each log page is written in a separate IO transfer (PageCount of 1) 9 | SinglePageIO = 0x0001, // RESTART_SINGLE_PAGE_IO 10 | 11 | /// Indicated that the volume is dismounted cleanly 12 | CleanDismount = 0x0002, // NTFS v3.1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/NTFSLogOperation.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | /// 5 | /// The comment specifies the record type which follows the record. 6 | /// 7 | public enum NTFSLogOperation : ushort 8 | { 9 | Noop = 0x0000, 10 | CompensationLogRecord = 0x0001, 11 | InitializeFileRecordSegment = 0x0002, // FILE_RECORD_SEGMENT 12 | DeallocateFileRecordSegment = 0x0003, 13 | WriteEndOfFileRecordSegment = 0x0004, // ATTRIBUTE_RECORD_HEADER 14 | CreateAttribute = 0x0005, // ATTRIBUTE_RECORD_HEADER 15 | DeleteAttribute = 0x0006, 16 | UpdateResidentAttributeValue = 0x0007, // (value) 17 | UpdateNonResidentAttributeValue = 0x0008, // (value) 18 | UpdateMappingPairs = 0x0009, // (value = mapping pairs bytes) 19 | DeleteDirtyClusters = 0x000A, // array of LCN_RANGE 20 | SetNewAttributeSizes = 0x000B, // NEW_ATTRIBUTE_SIZES 21 | AddIndexEntryToRoot = 0x000C, // INDEX_ENTRY 22 | DeleteIndexEntryFromRoot = 0x000D, // INDEX_ENTRY 23 | AddIndexEntryToAllocationBuffer = 0x000E, // INDEX_ENTRY 24 | DeleteIndexEntryFromAllocationBuffer = 0x000F, // INDEX_ENTRY 25 | WriteEndOfIndexBuffer = 0x0010, // INDEX_ENTRY 26 | SetIndexEntryVcnInRoot = 0x0011, // VCN 27 | SetIndexEntryVcnInAllocationBuffer = 0x0012, // VCN 28 | UpdateFileNameInRoot = 0x0013, // DUPLICATED_INFORMATION 29 | UpdateFileNameInAllocationBuffer = 0x0014, // DUPLICATED_INFORMATION 30 | SetBitsInNonResidentBitMap = 0x0015, // BITMAP_RANGE 31 | ClearBitsInNonResidentBitMap = 0x0016, // BITMAP_RANGE 32 | HotFix = 0x0017, 33 | EndTopLevelAction = 0x0018, 34 | PrepareTransaction = 0x0019, 35 | CommitTransaction = 0x001A, 36 | ForgetTransaction = 0x001B, 37 | OpenNonResidentAttribute = 0x001C, // OPEN_ATTRIBUTE_ENTRY (The attribute name is stored in the UndoData field) 38 | OpenAttributeTableDump = 0x001D, // OPEN_ATTRIBUTE_ENTRY restart table 39 | AttributeNamesDump = 0x001E, // ATTRIBUTE_NAME_ENTRY array 40 | DirtyPageTableDump = 0x001F, // DIRTY_PAGE_ENTRY restart table 41 | TransactionTableDump = 0x0020, // TRANSACTION_ENTRY restart table 42 | UpdateRecordDataInRoot = 0x0021, // (value) 43 | UpdateRecordDataInAllocationBuffer = 0x0022, // (value) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Enums/TransactionState.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.FileSystems.NTFS 3 | { 4 | public enum TransactionState : byte 5 | { 6 | Uninitialized = 0, 7 | Active = 1, 8 | Prepared = 2, 9 | Committed = 3, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/LogFile.Analysis.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | public partial class LogFile 12 | { 13 | public LfsRecord FindNextRecord(LfsRecord record, int clientIndex) 14 | { 15 | do 16 | { 17 | ulong nextLsn = CalculateNextLsn(record.ThisLsn, record.Length); 18 | if (!IsLsnInFile(nextLsn, clientIndex)) 19 | { 20 | return null; 21 | } 22 | 23 | try 24 | { 25 | record = ReadRecord(nextLsn); 26 | } 27 | catch 28 | { 29 | return null; 30 | } 31 | 32 | ushort clientSeqNumber = m_restartPage.RestartArea.LogClientArray[clientIndex].SeqNumber; 33 | if (record.ClientIndex == clientIndex && record.ClientSeqNumber == clientSeqNumber) 34 | { 35 | return record; 36 | } 37 | } 38 | while (true); 39 | } 40 | 41 | public List FindNextRecords(ulong lsn, int clientIndex) 42 | { 43 | LfsRecord record = ReadRecord(lsn); 44 | List result = new List(); 45 | do 46 | { 47 | record = FindNextRecord(record, clientIndex); 48 | if (record != null) 49 | { 50 | result.Add(record); 51 | } 52 | } 53 | while (record != null); 54 | return result; 55 | } 56 | 57 | public bool IsLsnInFile(ulong lsn, int clientIndex) 58 | { 59 | if (m_restartPage == null) 60 | { 61 | m_restartPage = ReadRestartPage(); 62 | } 63 | 64 | return (lsn >= m_restartPage.RestartArea.LogClientArray[clientIndex].OldestLsn && 65 | lsn <= m_restartPage.RestartArea.CurrentLsn); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/RestartTables/AttributeNameEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace DiskAccessLibrary.FileSystems.NTFS 13 | { 14 | /// 15 | /// ATTRIBUTE_NAME_ENTRY 16 | /// 17 | public class AttributeNameEntry 18 | { 19 | public const int FixedLength = 4; 20 | 21 | public ushort OpenAttributeOffset; // Offset of the attibute with this name in the open attribute table 22 | // ushort NameLength; 23 | public string Name; // Null terminated 24 | 25 | public AttributeNameEntry() 26 | { 27 | Name = String.Empty; 28 | } 29 | 30 | public AttributeNameEntry(byte[] buffer, int offset) 31 | { 32 | OpenAttributeOffset = LittleEndianConverter.ToUInt16(buffer, offset + 0x00); 33 | ushort nameLength = LittleEndianConverter.ToUInt16(buffer, offset + 0x02); 34 | Name = UnicodeEncoding.Unicode.GetString(buffer, offset + 0x04, nameLength); 35 | } 36 | 37 | public void WriteBytes(byte[] buffer, int offset) 38 | { 39 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x00, OpenAttributeOffset); 40 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x02, (ushort)(Name.Length * 2)); 41 | ByteWriter.WriteUTF16String(buffer, offset + 0x04, Name + "\0"); 42 | } 43 | 44 | public int Length 45 | { 46 | get 47 | { 48 | // All entries are null terminated except the terminating entry 49 | return FixedLength + (Name.Length + 1) * 2; 50 | } 51 | } 52 | 53 | public static List ReadTable(byte[] tableBytes) 54 | { 55 | List result = new List(); 56 | int offset = 0; 57 | while (offset < tableBytes.Length) 58 | { 59 | AttributeNameEntry entry = new AttributeNameEntry(tableBytes, offset); 60 | if (entry.OpenAttributeOffset == 0 || entry.Name.Length == 0) 61 | { 62 | break; 63 | } 64 | result.Add(entry); 65 | offset += entry.Length; 66 | } 67 | return result; 68 | } 69 | 70 | public static byte[] GetTableBytes(List entries) 71 | { 72 | int tableLength = 0; 73 | foreach (AttributeNameEntry entry in entries) 74 | { 75 | tableLength += entry.Length; 76 | } 77 | 78 | tableLength += AttributeNameEntry.FixedLength; // Terminating entry 79 | 80 | byte[] tableBytes = new byte[tableLength]; 81 | int offset = 0; 82 | foreach (AttributeNameEntry entry in entries) 83 | { 84 | entry.WriteBytes(tableBytes, offset); 85 | offset += entry.Length; 86 | } 87 | 88 | // No need to write the terminating entry, the 0's are already in place 89 | return tableBytes; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/RestartTables/RestartTableEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | public abstract class RestartTableEntry 12 | { 13 | public const uint RestartEntryAllocated = 0xFFFFFFFF; 14 | 15 | public uint AllocatedOrNextFree; 16 | 17 | public abstract void WriteBytes(byte[] buffer, int offset); 18 | 19 | public abstract int Length 20 | { 21 | get; 22 | } 23 | 24 | public static T ReadEntry(byte[] buffer, int offset, uint majorVersion) where T : RestartTableEntry 25 | { 26 | if (typeof(T) == typeof(DirtyPageEntry)) 27 | { 28 | return (T)(object)new DirtyPageEntry(buffer, offset, majorVersion); 29 | } 30 | else if (typeof(T) == typeof(OpenAttributeEntry)) 31 | { 32 | return (T)(object)new OpenAttributeEntry(buffer, offset, majorVersion); 33 | } 34 | else if (typeof(T) == typeof(TransactionEntry)) 35 | { 36 | return (T)(object)new TransactionEntry(buffer, offset); 37 | } 38 | else 39 | { 40 | throw new NotSupportedException(); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/RestartTables/RestartTableHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// RESTART_TABLE 13 | /// 14 | public class RestartTableHeader 15 | { 16 | public const int Length = 24; 17 | 18 | public ushort EntrySize; 19 | public ushort NumberEntries; 20 | public ushort NumberAllocated; 21 | // 6 reserved bytes 22 | public uint FreeGoal; 23 | public uint FirstFree; 24 | public uint LastFree; 25 | 26 | public RestartTableHeader() 27 | { 28 | } 29 | 30 | public RestartTableHeader(byte[] buffer, int offset) 31 | { 32 | EntrySize = LittleEndianConverter.ToUInt16(buffer, offset + 0x00); 33 | NumberEntries = LittleEndianConverter.ToUInt16(buffer, offset + 0x02); 34 | NumberAllocated = LittleEndianConverter.ToUInt16(buffer, offset + 0x04); 35 | FreeGoal = LittleEndianConverter.ToUInt32(buffer, offset + 0x0C); 36 | FirstFree = LittleEndianConverter.ToUInt32(buffer, offset + 0x10); 37 | LastFree = LittleEndianConverter.ToUInt32(buffer, offset + 0x14); 38 | } 39 | 40 | public void WriteBytes(byte[] buffer, int offset) 41 | { 42 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x00, EntrySize); 43 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x02, NumberEntries); 44 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x04, NumberAllocated); 45 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x0C, FreeGoal); 46 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x10, FirstFree); 47 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x14, LastFree); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/RestartTables/RestartTableHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using Utilities; 11 | 12 | namespace DiskAccessLibrary.FileSystems.NTFS 13 | { 14 | public class RestartTableHelper 15 | { 16 | public static bool IsPointerValid(uint nextFree, int entrySize, int tableSize) 17 | { 18 | if (nextFree == 0) 19 | { 20 | return true; 21 | } 22 | 23 | return (nextFree >= RestartTableHeader.Length) || ((nextFree - RestartTableHeader.Length) % entrySize == 0) || (nextFree < tableSize); 24 | } 25 | 26 | public static List ReadTable(byte[] tableBytes, uint majorVersion) where T : RestartTableEntry 27 | { 28 | List result = new List(); 29 | RestartTableHeader header = new RestartTableHeader(tableBytes, 0); 30 | int offset = RestartTableHeader.Length; 31 | while (offset < tableBytes.Length) 32 | { 33 | uint allocatedOrNextFree = LittleEndianConverter.ToUInt32(tableBytes, offset + 0x00); 34 | if (allocatedOrNextFree == RestartTableEntry.RestartEntryAllocated) 35 | { 36 | T entry = RestartTableEntry.ReadEntry(tableBytes, offset, majorVersion); 37 | result.Add(entry); 38 | } 39 | else if (!IsPointerValid(allocatedOrNextFree, header.EntrySize, tableBytes.Length)) 40 | { 41 | throw new InvalidDataException("Invalid restart table entry, AllocatedOrNextFree points to invalid location"); 42 | } 43 | offset += header.EntrySize; 44 | } 45 | return result; 46 | } 47 | 48 | public static byte[] GetTableBytes(List entries) where T : RestartTableEntry 49 | { 50 | int tableLength = RestartTableHeader.Length; 51 | int entryLength = 0; 52 | foreach (T entry in entries) 53 | { 54 | if (entry.Length > entryLength) 55 | { 56 | entryLength = entry.Length; 57 | } 58 | } 59 | tableLength += entries.Count * entryLength; 60 | byte[] tableBytes = new byte[tableLength]; 61 | RestartTableHeader header = new RestartTableHeader(); 62 | header.EntrySize = (ushort)entryLength; 63 | header.NumberEntries = (ushort)entries.Count; 64 | header.NumberAllocated = (ushort)entries.Count; 65 | header.FreeGoal = UInt32.MaxValue; 66 | header.FirstFree = 0; 67 | header.LastFree = 0; 68 | header.WriteBytes(tableBytes, 0); 69 | for (int index = 0; index < entries.Count; index++) 70 | { 71 | entries[index].AllocatedOrNextFree = RestartTableEntry.RestartEntryAllocated; 72 | entries[index].WriteBytes(tableBytes, RestartTableHeader.Length + index * entryLength); 73 | } 74 | return tableBytes; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/RestartTables/TransactionEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2019 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// TRANSACTION_ENTRY 13 | /// 14 | public class TransactionEntry : RestartTableEntry 15 | { 16 | public const int EntryLength = 0x28; 17 | 18 | // uint AllocatedOrNextFree; 19 | public TransactionState TransactionState; 20 | // 3 reserved bytes 21 | public ulong FirstLsn; // First LSN for the transaction 22 | public ulong PreviousLsn; 23 | public ulong UndoNextLsn; 24 | public uint UndoRecords; // Number of undo log records pending abort 25 | public int UndoBytes; // Number of bytes in undo log records pending abort 26 | 27 | public TransactionEntry() 28 | { 29 | } 30 | 31 | public TransactionEntry(byte[] buffer, int offset) 32 | { 33 | AllocatedOrNextFree = LittleEndianConverter.ToUInt32(buffer, offset + 0x00); 34 | TransactionState = (TransactionState)ByteReader.ReadByte(buffer, offset + 0x04); 35 | FirstLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x08); 36 | PreviousLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x10); 37 | UndoNextLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x18); 38 | UndoRecords = LittleEndianConverter.ToUInt32(buffer, offset + 0x20); 39 | UndoBytes = LittleEndianConverter.ToInt32(buffer, offset + 0x24); 40 | } 41 | 42 | public override void WriteBytes(byte[] buffer, int offset) 43 | { 44 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x00, AllocatedOrNextFree); 45 | ByteWriter.WriteByte(buffer, offset + 0x04, (byte)TransactionState); 46 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x08, FirstLsn); 47 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x10, PreviousLsn); 48 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x18, UndoNextLsn); 49 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x20, UndoRecords); 50 | LittleEndianWriter.WriteInt32(buffer, offset + 0x24, UndoBytes); 51 | } 52 | 53 | public override int Length 54 | { 55 | get 56 | { 57 | return EntryLength; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Structures/BitmapRange.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// BITMAP_RANGE 13 | /// 14 | internal class BitmapRange 15 | { 16 | public const int Length = 8; 17 | 18 | public uint BitmapOffset; 19 | public uint NumberOfBits; 20 | 21 | public BitmapRange(uint bitmapOffset, uint numberOfBits) 22 | { 23 | BitmapOffset = bitmapOffset; 24 | NumberOfBits = numberOfBits; 25 | } 26 | 27 | public BitmapRange(byte[] buffer, int offset) 28 | { 29 | BitmapOffset = LittleEndianConverter.ToUInt32(buffer, offset + 0x00); 30 | NumberOfBits = LittleEndianConverter.ToUInt32(buffer, offset + 0x04); 31 | } 32 | 33 | public void WriteBytes(byte[] buffer, int offset) 34 | { 35 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x00, BitmapOffset); 36 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x04, NumberOfBits); 37 | } 38 | 39 | public byte[] GetBytes() 40 | { 41 | byte[] buffer = new byte[Length]; 42 | WriteBytes(buffer, 0); 43 | return buffer; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Log/Structures/LfsClientRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// LFS_CLIENT_RECORD 13 | /// 14 | public class LfsClientRecord 15 | { 16 | public const int ClientNameMaxLength = 64; // 64 unicode characters 17 | public const int Length = 0x20 + ClientNameMaxLength * 2; 18 | 19 | public ulong OldestLsn; 20 | public ulong ClientRestartLsn; 21 | public ushort PrevClient; 22 | public ushort NextClient; 23 | public ushort SeqNumber; 24 | // ushort AlignWord 25 | // uint AlignDWord 26 | // uint ClientNameLength // Number of bytes 27 | public string ClientName; 28 | 29 | public LfsClientRecord(string clientName) 30 | { 31 | ClientName = clientName; 32 | } 33 | 34 | public LfsClientRecord(byte[] buffer, int offset) 35 | { 36 | OldestLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x00); 37 | ClientRestartLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x08); 38 | PrevClient = LittleEndianConverter.ToUInt16(buffer, offset + 0x10); 39 | NextClient = LittleEndianConverter.ToUInt16(buffer, offset + 0x12); 40 | SeqNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0x14); 41 | uint clientNameLength = LittleEndianConverter.ToUInt16(buffer, offset + 0x1C); 42 | ClientName = ByteReader.ReadUTF16String(buffer, offset + 0x20, (int)(clientNameLength / 2)); 43 | } 44 | 45 | public void WriteBytes(byte[] buffer, int offset) 46 | { 47 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x00, OldestLsn); 48 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x08, ClientRestartLsn); 49 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x10, PrevClient); 50 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x12, NextClient); 51 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x14, SeqNumber); 52 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x1C, (uint)(ClientName.Length * 2)); 53 | ByteWriter.WriteUTF16String(buffer, offset + 0x20, ClientName); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/NTFSVolume.Extend.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using Utilities; 10 | 11 | namespace DiskAccessLibrary.FileSystems.NTFS 12 | { 13 | public partial class NTFSVolume : IExtendableFileSystem 14 | { 15 | public long GetMaximumSizeToExtend() 16 | { 17 | // The sector following the NTFS volume is a backup to the boot sector, we want to leave room for a new backup boot sector 18 | return m_volume.Size - (this.Size + m_volume.BytesPerSector); 19 | } 20 | 21 | public void Extend(long numberOfAdditionalSectors) 22 | { 23 | long originalNumberOfSectors = (long)m_bootRecord.TotalSectors; 24 | long numberOfAdditionalClusters = numberOfAdditionalSectors / m_bootRecord.SectorsPerCluster; 25 | 26 | m_bitmap.Extend(numberOfAdditionalClusters); 27 | 28 | // We set TotalSectors only after extending the File system, or otherwise the $bitmap size will mismatch 29 | m_bootRecord.TotalSectors += (ulong)numberOfAdditionalClusters * m_bootRecord.SectorsPerCluster; // We only add usable sectors 30 | 31 | // Update boot sector 32 | byte[] bootRecordBytes = m_bootRecord.GetBytes(); 33 | WriteSectors(0, bootRecordBytes, ContentType.FileData); 34 | 35 | // Recreate the backup boot sector at the new end of the raw volume 36 | // Note: The backup boot sector does not count as part of the NTFS volume 37 | long backupBootSectorIndex = originalNumberOfSectors + numberOfAdditionalSectors; 38 | WriteSectors(backupBootSectorIndex, bootRecordBytes, ContentType.FileData); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/NTFSVolume.Recovery.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using Utilities; 11 | 12 | namespace DiskAccessLibrary.FileSystems.NTFS 13 | { 14 | public partial class NTFSVolume 15 | { 16 | /// 17 | /// This method is slower and should only be used for recovery purposes. 18 | /// 19 | public List GetFileRecordsInDirectoryFromMft(long directoryBaseSegmentNumber) 20 | { 21 | return GetFileRecordsInDirectoryFromMft(directoryBaseSegmentNumber, false); 22 | } 23 | 24 | /// 25 | /// This method is slower and should only be used for recovery purposes. 26 | /// 27 | private List GetFileRecordsInDirectoryFromMft(long directoryBaseSegmentNumber, bool includeMetaFiles) 28 | { 29 | long maxNumOfRecords = m_mft.GetNumberOfUsableSegments(); 30 | List result = new List(); 31 | 32 | for (long index = 0; index < maxNumOfRecords; index++) 33 | { 34 | FileRecord record; 35 | try 36 | { 37 | record = m_mft.GetFileRecord(index); 38 | } 39 | catch (InvalidDataException) 40 | { 41 | continue; 42 | } 43 | if (record != null) 44 | { 45 | if (record.ParentDirectoryReference.SegmentNumber == directoryBaseSegmentNumber) 46 | { 47 | if (record.IsInUse && (includeMetaFiles || !record.IsMetaFile)) 48 | { 49 | result.Add(record); 50 | } 51 | } 52 | } 53 | } 54 | 55 | return result; 56 | } 57 | 58 | /// 59 | /// This method is slower and should only be used for recovery purposes. 60 | /// 61 | private KeyValuePairList GetFileNameRecordsInDirectoryFromMft(long directoryBaseSegmentNumber) 62 | { 63 | KeyValuePairList result = new KeyValuePairList(); 64 | List fileRecords = GetFileRecordsInDirectoryFromMft(directoryBaseSegmentNumber); 65 | foreach (FileRecord fileRecord in fileRecords) 66 | { 67 | result.Add(fileRecord.BaseSegmentReference, fileRecord.FileNameRecord); 68 | } 69 | return result; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Structures/AttributeDefinitionEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// ATTRIBUTE_DEFINITION_COLUMNS 13 | /// 14 | public class AttributeDefinitionEntry 15 | { 16 | public const int Length = 160; 17 | private const int AttributeNameLength = 64; // Number of characters 18 | 19 | public string AttributeName; 20 | public AttributeType AttributeType; 21 | public uint DisplayRule; 22 | public CollationRule CollationRule; 23 | public AttributeDefinitionFlags Flags; 24 | public ulong MinimumLength; 25 | public ulong MaximumLength; 26 | 27 | public AttributeDefinitionEntry() 28 | { 29 | } 30 | 31 | public AttributeDefinitionEntry(string attributeName, AttributeType attributeType, AttributeDefinitionFlags flags, ulong minimumLength, ulong maximumLength) 32 | { 33 | AttributeName = attributeName; 34 | AttributeType = attributeType; 35 | Flags = flags; 36 | MinimumLength = minimumLength; 37 | MaximumLength = maximumLength; 38 | } 39 | 40 | public AttributeDefinitionEntry(byte[] buffer, int offset) 41 | { 42 | AttributeName = ByteReader.ReadUTF16String(buffer, offset + 0x00, AttributeNameLength).TrimEnd(new char[] { '\0' }); 43 | AttributeType = (AttributeType)LittleEndianConverter.ToUInt32(buffer, offset + 0x80); 44 | DisplayRule = LittleEndianConverter.ToUInt32(buffer, offset + 0x84); 45 | CollationRule = (CollationRule)LittleEndianConverter.ToUInt32(buffer, offset + 0x88); 46 | Flags = (AttributeDefinitionFlags)LittleEndianConverter.ToUInt32(buffer, offset + 0x8C); 47 | MinimumLength = LittleEndianConverter.ToUInt64(buffer, offset + 0x90); 48 | MaximumLength = LittleEndianConverter.ToUInt64(buffer, offset + 0x98); 49 | } 50 | 51 | public void WriteBytes(byte[] buffer, int offset) 52 | { 53 | ByteWriter.WriteUTF16String(buffer, offset + 0x00, AttributeName.PadRight(AttributeNameLength, '\0'), AttributeNameLength); 54 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x80, (uint)AttributeType); 55 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x84, DisplayRule); 56 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x88, (uint)CollationRule); 57 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x8C, (uint)Flags); 58 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x90, MinimumLength); 59 | LittleEndianWriter.WriteUInt64(buffer, offset + 0x98, MaximumLength); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Structures/DuplicatedInformation.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using Utilities; 10 | 11 | namespace DiskAccessLibrary.FileSystems.NTFS 12 | { 13 | /// 14 | /// DUPLICATED_INFORMATION 15 | /// 16 | public class DuplicatedInformation 17 | { 18 | public const int Length = 0x38; 19 | 20 | public DateTime CreationTime; 21 | public DateTime ModificationTime; 22 | public DateTime MftModificationTime; 23 | public DateTime LastAccessTime; 24 | public ulong AllocatedLength; // of the file 25 | public ulong FileSize; // of the file 26 | public FileAttributes FileAttributes; 27 | public ushort PackedEASize; 28 | // ushort Reserved; 29 | 30 | public DuplicatedInformation() 31 | { 32 | } 33 | 34 | public DuplicatedInformation(byte[] buffer, int offset) 35 | { 36 | CreationTime = StandardInformationRecord.ReadDateTime(buffer, offset + 0x00); 37 | ModificationTime = StandardInformationRecord.ReadDateTime(buffer, offset + 0x08); 38 | MftModificationTime = StandardInformationRecord.ReadDateTime(buffer, offset + 0x10); 39 | LastAccessTime = StandardInformationRecord.ReadDateTime(buffer, offset + 0x18); 40 | AllocatedLength = LittleEndianConverter.ToUInt64(buffer, offset + 0x20); 41 | FileSize = LittleEndianConverter.ToUInt64(buffer, offset + 0x28); 42 | FileAttributes = (FileAttributes)LittleEndianConverter.ToUInt32(buffer, offset + 0x30); 43 | PackedEASize = LittleEndianConverter.ToUInt16(buffer, offset + 0x34); 44 | } 45 | 46 | public byte[] GetBytes() 47 | { 48 | byte[] buffer = new byte[Length]; 49 | 50 | StandardInformationRecord.WriteDateTime(buffer, 0x00, CreationTime); 51 | StandardInformationRecord.WriteDateTime(buffer, 0x08, ModificationTime); 52 | StandardInformationRecord.WriteDateTime(buffer, 0x10, MftModificationTime); 53 | StandardInformationRecord.WriteDateTime(buffer, 0x18, LastAccessTime); 54 | LittleEndianWriter.WriteUInt64(buffer, 0x20, AllocatedLength); 55 | LittleEndianWriter.WriteUInt64(buffer, 0x28, FileSize); 56 | LittleEndianWriter.WriteUInt32(buffer, 0x30, (uint)FileAttributes); 57 | LittleEndianWriter.WriteUInt16(buffer, 0x34, PackedEASize); 58 | 59 | return buffer; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DiskAccessLibrary/FileSystems/NTFS/Structures/MultiSectorHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2019 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using Utilities; 8 | 9 | namespace DiskAccessLibrary.FileSystems.NTFS 10 | { 11 | /// 12 | /// MULTI_SECTOR_HEADER: https://docs.microsoft.com/en-us/windows/desktop/devnotes/multi-sector-header 13 | /// 14 | public class MultiSectorHeader 15 | { 16 | public const int Length = 8; 17 | internal const int SignatureLength = 4; 18 | 19 | private string m_signature; // 4 bytes 20 | private ushort m_updateSequenceArrayOffset; 21 | private ushort m_updateSequenceArraySize; // The number of USHORT entries (The USN and the missing 2 bytes from each stride) 22 | 23 | public MultiSectorHeader(string signature, ushort updateSequenceArrayOffset, ushort updateSequenceArraySize) 24 | { 25 | m_signature = signature; 26 | m_updateSequenceArrayOffset = updateSequenceArrayOffset; 27 | m_updateSequenceArraySize = updateSequenceArraySize; 28 | } 29 | 30 | public MultiSectorHeader(byte[] buffer, int offset) 31 | { 32 | m_signature = ByteReader.ReadAnsiString(buffer, offset + 0x00, SignatureLength); 33 | m_updateSequenceArrayOffset = LittleEndianConverter.ToUInt16(buffer, offset + 0x04); 34 | m_updateSequenceArraySize = LittleEndianConverter.ToUInt16(buffer, offset + 0x06); 35 | } 36 | 37 | public void WriteBytes(byte[] buffer, int offset) 38 | { 39 | ByteWriter.WriteAnsiString(buffer, offset + 0x00, m_signature, SignatureLength); 40 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x04, m_updateSequenceArrayOffset); 41 | LittleEndianWriter.WriteUInt16(buffer, offset + 0x06, m_updateSequenceArraySize); 42 | } 43 | 44 | public string Signature 45 | { 46 | get 47 | { 48 | return m_signature; 49 | } 50 | } 51 | 52 | public ushort UpdateSequenceArrayOffset 53 | { 54 | get 55 | { 56 | return m_updateSequenceArrayOffset; 57 | } 58 | } 59 | 60 | public ushort UpdateSequenceArraySize 61 | { 62 | get 63 | { 64 | return m_updateSequenceArraySize; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Helpers/DiskExtentHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DiskExtentHelper 12 | { 13 | public static DiskExtent GetAlignedDiskExtent(DiskExtent extent, long alignInSectors) 14 | { 15 | long alignedStartSector = (long)Math.Ceiling((double)extent.FirstSector / alignInSectors) * alignInSectors; 16 | long lossDueToAlignment = (alignedStartSector - extent.FirstSector) * extent.BytesPerSector; 17 | return new DiskExtent(extent.Disk, alignedStartSector, extent.Size - lossDueToAlignment); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /DiskAccessLibrary/Helpers/DiskExtentsHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public class DiskExtentsHelper 12 | { 13 | /// In bytes 14 | internal static List GetUnallocatedExtents(Disk disk, long dataRegionStartSector, long dataRegionSize, List usedExtents) 15 | { 16 | List result = new List(); 17 | long startSector = dataRegionStartSector; 18 | SortExtentsByFirstSector(usedExtents); 19 | // see if there is room before each extent 20 | foreach (DiskExtent extent in usedExtents) 21 | { 22 | long extentStartSector = extent.FirstSector; 23 | long nextStartSector = extent.FirstSector + extent.Size / disk.BytesPerSector; 24 | long freeSpaceInBytes = (extentStartSector - startSector) * disk.BytesPerSector; 25 | if (freeSpaceInBytes > 0) 26 | { 27 | result.Add(new DiskExtent(disk, startSector, freeSpaceInBytes)); 28 | } 29 | 30 | startSector = nextStartSector; 31 | } 32 | 33 | // see if there is room after the last extent 34 | long spaceInBytes = dataRegionSize - (startSector - dataRegionStartSector) * disk.BytesPerSector; 35 | if (spaceInBytes > 0) 36 | { 37 | result.Add(new DiskExtent(disk, startSector, spaceInBytes)); 38 | } 39 | return result; 40 | } 41 | 42 | /// 43 | /// Sort (in-place) extents by first sector 44 | /// 45 | public static void SortExtentsByFirstSector(List extents) 46 | { 47 | SortedList list = new SortedList(); 48 | foreach (DiskExtent extent in extents) 49 | { 50 | list.Add(extent.FirstSector, extent); 51 | } 52 | 53 | extents.Clear(); 54 | extents.AddRange(list.Values); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Helpers/Settings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary 9 | { 10 | public class Settings 11 | { 12 | // ReadFile failed with ERROR_NO_SYSTEM_RESOURCES when reading 65480 (512-byte) sectors or more using overlapped IO. 13 | // Note: The benefits beyond 16MB buffer are not very significant, 14 | // e.g. 512MB will only give ~5.6% speed boost over 64MB, ~7.9% over 32MB, and ~10.1% over 16MB. 15 | public static int MaximumTransferSizeLBA = 32768; // 16 MB (assuming 512-byte sectors) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/DatabaseRecords/DatabaseRecordFragment.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using Utilities; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | // This class represents a single VBLK block (record can span multiple VBLK blocks) 13 | public class DatabaseRecordFragment 14 | { 15 | public const int HeaderLength = 16; 16 | 17 | public string Signature = "VBLK"; // VBLK 18 | public uint SequenceNumber; // each fragment have different SequenceNumber, SequenceNumber starts from 4 ( 0-3 are taken by the VMDB) 19 | public uint GroupNumber; // same for all fragments of the same record 20 | public ushort NumberInGroup; // (x of y), Zero-based 21 | public ushort FragmentCount; // Number of fragments in group 22 | public byte[] Data; 23 | 24 | public DatabaseRecordFragment() 25 | { 26 | 27 | } 28 | 29 | protected DatabaseRecordFragment(byte[] buffer) 30 | { 31 | Signature = ByteReader.ReadAnsiString(buffer, 0x00, 4); 32 | SequenceNumber = BigEndianConverter.ToUInt32(buffer, 0x04); 33 | GroupNumber = BigEndianConverter.ToUInt32(buffer, 0x08); 34 | NumberInGroup = BigEndianConverter.ToUInt16(buffer, 0x0C); 35 | FragmentCount = BigEndianConverter.ToUInt16(buffer, 0x0E); 36 | Data = new byte[buffer.Length - HeaderLength]; 37 | Array.Copy(buffer, 0x10, Data, 0, buffer.Length - HeaderLength); 38 | } 39 | 40 | public byte[] GetBytes(int blockSize) 41 | { 42 | byte[] buffer = new byte[blockSize]; 43 | ByteWriter.WriteAnsiString(buffer, 0, Signature, 4); 44 | BigEndianWriter.WriteUInt32(buffer, 0x04, SequenceNumber); 45 | BigEndianWriter.WriteUInt32(buffer, 0x08, GroupNumber); 46 | BigEndianWriter.WriteUInt16(buffer, 0x0C, NumberInGroup); 47 | BigEndianWriter.WriteUInt16(buffer, 0x0E, FragmentCount); 48 | ByteWriter.WriteBytes(buffer, 0x10, Data, Math.Min(Data.Length, blockSize - HeaderLength)); 49 | return buffer; 50 | } 51 | 52 | public static DatabaseRecordFragment GetDatabaseRecordFragment(byte[] buffer) 53 | { 54 | string signature = ByteReader.ReadAnsiString(buffer, 0x00, 4); 55 | ushort fragmentCount = BigEndianConverter.ToUInt16(buffer, 0x0E); 56 | if (fragmentCount == 0 || signature != "VBLK") 57 | { 58 | return null; 59 | } 60 | else 61 | { 62 | return new DatabaseRecordFragment(buffer); 63 | } 64 | } 65 | 66 | /// 67 | /// Free fragment from record data (the result can be used to overwrite this fragment) 68 | /// 69 | public void Clear() 70 | { 71 | this.GroupNumber = 0; 72 | this.NumberInGroup = 0; 73 | this.FragmentCount = 0; 74 | this.Data = new byte[0]; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/DynamicDiskExtent.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary.LogicalDiskManager 10 | { 11 | public class DynamicDiskExtent : DiskExtent 12 | { 13 | private ulong m_extentID; 14 | public string Name; 15 | public Guid DiskGuid; 16 | 17 | public DynamicDiskExtent(Disk disk, long firstSector, long size, ulong extentID) : base(disk, firstSector, size) 18 | { 19 | m_extentID = extentID; 20 | } 21 | 22 | public DynamicDiskExtent(DiskExtent diskExtent, ulong extentID) : base(diskExtent.Disk, diskExtent.FirstSector, diskExtent.Size) 23 | { 24 | m_extentID = extentID; 25 | } 26 | 27 | public ulong ExtentID 28 | { 29 | get 30 | { 31 | return m_extentID; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/DatabaseHeaderUpdateStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | // as reported by DMDiag 5 | public enum DatabaseHeaderUpdateStatus : ushort 6 | { 7 | Clean = 0x01, // consistant state 8 | Change = 0x02, // in a creation phase / during update 9 | Commit = 0x03, // replaces 'Change' header and comes immediately before 'Clean' header 10 | Abort = 0x04, 11 | New = 0x05, 12 | Stale = 0x06, 13 | Offline = 0x08, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/DatabaseRecordUpdateStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | public enum DatabaseRecordUpdateStatus : ushort 5 | { 6 | Active = 0x00, // consistant state 7 | ActivePendingDeletion = 0x01, // about to be deleted, but is still active 8 | PendingActivation = 0x02, // just been created, but it is not yet active 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/ExtentLayoutName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | public enum ExtentLayoutName : byte 5 | { 6 | Stripe = 1, 7 | Concatenated = 2, 8 | RAID5 = 3, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/KernelUpdateLogEntryStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | public enum KernelUpdateLogEntryStatus : byte 5 | { 6 | NotExist = 0x00, 7 | Detach = 0x01, // as reported by DMDIAG 8 | Dirty = 0x02, // as reported by DMDIAG 9 | Commit = 0x03, // as reported by DMDIAG 10 | LogDetach = 0x04, // as reported by DMDIAG 11 | // APP_DIRTY = 0x05 (as reported by DMDIAG, which also reports 'recover_seqno 0' for this entry) 12 | // DMDIAG reports > 0x05 as INVALID 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/ReadPolicyName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | // DMDiag reports this as read policy 5 | // Names as reported by DMDiag 6 | public enum ReadPolicyName : byte 7 | { 8 | Round = 0x01, 9 | Prefer = 0x02, 10 | Select = 0x03, // Use this for simple volumes 11 | RAID = 0x04, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/RecordType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary.LogicalDiskManager 3 | { 4 | public enum RecordType : byte 5 | { 6 | None = 0, 7 | Volume = 1, 8 | Component = 2, 9 | Extent = 3, // partition 10 | Disk = 4, 11 | DiskGroup = 5 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Enums/VolumeFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DiskAccessLibrary.LogicalDiskManager 4 | { 5 | // As reported by DMDiag: 6 | [Flags] 7 | public enum VolumeFlags : uint 8 | { 9 | Writeback = 0x000001, // Set by default 10 | Writecopy = 0x000002, // Set by default 11 | Crashed = 0x000004, // 12 | DefaultUnknown = 0x000010, // Set by default 13 | BadLog = 0x000100, 14 | KDetach = 0x000400, 15 | Storage = 0x000800, 16 | AppRecover = 0x001000, 17 | Pending = 0x002000, 18 | RaidNtft = 0x100000, 19 | BootVolume = 0x200000, 20 | SystemVolume = 0x400000, 21 | RetainPartition = 0x800000, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Exceptions/MissingDatabaseRecordException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace DiskAccessLibrary.LogicalDiskManager 10 | { 11 | public class MissingDatabaseRecordException : Exception 12 | { 13 | public MissingDatabaseRecordException() : base() 14 | { 15 | } 16 | 17 | public MissingDatabaseRecordException(string message) : base(message) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Helpers/DynamicDiskExtentHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | public class DynamicDiskExtentHelper 13 | { 14 | public static int GetIndexOfExtentID(List extents, ulong extentID) 15 | { 16 | for (int index = 0; index < extents.Count; index++) 17 | { 18 | if (extents[index].ExtentID == extentID) 19 | { 20 | return index; 21 | } 22 | } 23 | return -1; 24 | } 25 | 26 | public static DynamicDiskExtent GetByExtentID(List extents, ulong extentID) 27 | { 28 | int index = GetIndexOfExtentID(extents, extentID); 29 | if (index >= 0) 30 | { 31 | return extents[index]; 32 | } 33 | else 34 | { 35 | return null; 36 | } 37 | } 38 | 39 | /// 40 | /// Support null disks 41 | /// 42 | public static DynamicDiskExtent GetDiskExtent(DynamicDisk dynamicDisk, ExtentRecord extentRecord) 43 | { 44 | long extentStartSector = GetExtentStartSector(dynamicDisk, extentRecord); 45 | long extentSize = (long)extentRecord.SizeLBA * PublicRegionHelper.BytesPerPublicRegionSector; 46 | Disk disk = null; 47 | Guid diskGuid = Guid.Empty; 48 | if (dynamicDisk != null) 49 | { 50 | disk = dynamicDisk.Disk; 51 | diskGuid = dynamicDisk.DiskGuid; 52 | } 53 | DynamicDiskExtent extent = new DynamicDiskExtent(disk, extentStartSector, extentSize, extentRecord.ExtentId); 54 | extent.Name = extentRecord.Name; 55 | extent.DiskGuid = diskGuid; 56 | return extent; 57 | } 58 | 59 | /// 60 | /// Support null disks 61 | /// 62 | public static long GetExtentStartSector(DynamicDisk disk, ExtentRecord extentRecord) 63 | { 64 | long publicRegionStartLBA = 0; 65 | int bytesPerDiskSector = DynamicColumn.DefaultBytesPerSector; // default for missing disks 66 | if (disk != null) 67 | { 68 | bytesPerDiskSector = disk.BytesPerSector; 69 | PrivateHeader privateHeader = disk.PrivateHeader; 70 | publicRegionStartLBA = (long)privateHeader.PublicRegionStartLBA; 71 | } 72 | return PublicRegionHelper.TranslateFromPublicRegionLBA((long)extentRecord.DiskOffsetLBA, publicRegionStartLBA, bytesPerDiskSector); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Helpers/DynamicDiskExtentsHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace DiskAccessLibrary.LogicalDiskManager 10 | { 11 | public class DynamicDiskExtentsHelper 12 | { 13 | /// 14 | /// Sort (in-place) extents by first sector 15 | /// 16 | public static void SortExtentsByFirstSector(List extents) 17 | { 18 | SortedList list = new SortedList(); 19 | foreach (DynamicDiskExtent extent in extents) 20 | { 21 | list.Add(extent.FirstSector, extent); 22 | } 23 | 24 | extents.Clear(); 25 | extents.AddRange(list.Values); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Helpers/DynamicDiskHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | public partial class DynamicDiskHelper 13 | { 14 | public static DynamicDisk FindDisk(List disks, Guid diskGuid) 15 | { 16 | foreach (DynamicDisk dynamicDisk in disks) 17 | { 18 | if (dynamicDisk.DiskGuid == diskGuid) 19 | { 20 | return dynamicDisk; 21 | } 22 | } 23 | return null; 24 | } 25 | 26 | public static List FindDiskGroup(List disks, Guid diskGroupGuid) 27 | { 28 | List result = new List(); 29 | foreach (DynamicDisk dynamicDisk in disks) 30 | { 31 | if (dynamicDisk.DiskGroupGuid == diskGroupGuid) 32 | { 33 | result.Add(dynamicDisk); 34 | } 35 | } 36 | return result; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Helpers/PublicRegionHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary.LogicalDiskManager 9 | { 10 | public class PublicRegionHelper 11 | { 12 | /// 13 | /// LBA values in extent / volume / component records refer to logical 512-byte blocks within the public region of the disk, regardless of the actual block size of the disk. 14 | /// We have to translate those values in order to support disks with 4K sectors. 15 | /// 16 | public const int BytesPerPublicRegionSector = 512; 17 | 18 | public static long TranslateFromPublicRegionLBA(long publicRegionOffsetLBA, long publicRegionStartLBA, int bytesPerDiskSector) 19 | { 20 | return publicRegionStartLBA + publicRegionOffsetLBA * BytesPerPublicRegionSector / bytesPerDiskSector; 21 | } 22 | 23 | public static long TranslateFromPublicRegionSizeLBA(long sectorCount, int bytesPerDiskSector) 24 | { 25 | return sectorCount * BytesPerPublicRegionSector / bytesPerDiskSector; 26 | } 27 | 28 | public static long TranslateToPublicRegionLBA(long sectorIndex, PrivateHeader privateHeader) 29 | { 30 | return TranslateToPublicRegionLBA(sectorIndex, (long)privateHeader.PublicRegionStartLBA, (int)privateHeader.BytesPerSector); 31 | } 32 | 33 | public static long TranslateToPublicRegionLBA(long sectorIndex, long publicRegionStartLBA, int bytesPerDiskSector) 34 | { 35 | return (sectorIndex - publicRegionStartLBA) * bytesPerDiskSector / BytesPerPublicRegionSector; 36 | } 37 | 38 | public static long TranslateToPublicRegionSizeLBA(long sectorCount, PrivateHeader privateHeader) 39 | { 40 | return TranslateToPublicRegionSizeLBA(sectorCount, (int)privateHeader.BytesPerSector); 41 | } 42 | 43 | public static long TranslateToPublicRegionSizeLBA(long sectorCount, int bytesPerDiskSector) 44 | { 45 | return sectorCount * bytesPerDiskSector / BytesPerPublicRegionSector; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/KernelUpdateLog/KernalUpdateLog.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | public class KernelUpdateLog 13 | { 14 | List m_pages = new List(); 15 | 16 | public KernelUpdateLog(List pages) 17 | { 18 | m_pages = pages; 19 | } 20 | 21 | public void SetLastEntry(DynamicDisk databaseDisk, ulong committedTransactionID, ulong pendingTransactionID) 22 | { 23 | SetLastEntry(databaseDisk.Disk, databaseDisk.PrivateHeader, databaseDisk.TOCBlock, committedTransactionID, pendingTransactionID); 24 | } 25 | 26 | public void SetLastEntry(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock, ulong committedTransactionID, ulong pendingTransactionID) 27 | { 28 | if (m_pages.Count > 0) 29 | { 30 | m_pages[0].SetLastEntry(committedTransactionID, pendingTransactionID); 31 | // Windows kernel stores the last committedTransactionID / pendingTransactionID in memory, 32 | // and it will overwrite the values we write as soon as dmadmin is started, 33 | // However, it doesn't seem to cause any issues 34 | KernelUpdateLogPage.WriteToDisk(disk, privateHeader, tocBlock, m_pages[0]); 35 | } 36 | else 37 | { 38 | throw new InvalidOperationException("KLog records have not been previously read from disk"); 39 | } 40 | } 41 | 42 | public static KernelUpdateLog ReadFromDisk(DynamicDisk databaseDisk) 43 | { 44 | return ReadFromDisk(databaseDisk.Disk, databaseDisk.PrivateHeader, databaseDisk.TOCBlock); 45 | } 46 | 47 | public static KernelUpdateLog ReadFromDisk(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock) 48 | { 49 | List pages = new List(); 50 | KernelUpdateLogPage firstPage = KernelUpdateLogPage.ReadFromDisk(disk, privateHeader, tocBlock, 0); 51 | pages.Add(firstPage); 52 | for (int index = 1; index < firstPage.NumberOfPages; index++) 53 | { 54 | KernelUpdateLogPage page = KernelUpdateLogPage.ReadFromDisk(disk, privateHeader, tocBlock, index); 55 | pages.Add(page); 56 | } 57 | return new KernelUpdateLog(pages); 58 | } 59 | 60 | public List Pages 61 | { 62 | get 63 | { 64 | return m_pages; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/TOCBlock/TOCRegion.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using Utilities; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | [Flags] 13 | public enum TOCRegionFlags : ushort 14 | { 15 | NotExist = 0x01, // The present of this flag hide the region from display in DMDiag 16 | New = 0x02, // As reported by DMDiag 17 | Delete = 0x04, // As reported by DMDiag 18 | Disabled = 0x08 // As reported by DMDiag 19 | } 20 | 21 | public class TOCRegion 22 | { 23 | public const int Length = 34; 24 | 25 | public string Name; // 'config' or 'log' (8 characters max) 26 | public TOCRegionFlags RegionFlags; 27 | public ulong StartLBA; // Sector Offset from PrivateRegionStart 28 | public ulong SizeLBA; // Size of the region 29 | public ushort Unknown; // 00 06 ( other values will make DMDiag to report the database as invalid ) 30 | public ushort CopyNumber; // copy number, always 00 01 31 | // 4 zero bytes 32 | 33 | public TOCRegion(byte[] buffer, int offset) 34 | { 35 | Name = ByteReader.ReadAnsiString(buffer, offset + 0x00, 8).Trim('\0'); 36 | RegionFlags = (TOCRegionFlags)BigEndianConverter.ToUInt16(buffer, offset + 0x08); 37 | StartLBA = BigEndianConverter.ToUInt64(buffer, offset + 0x0A); 38 | SizeLBA = BigEndianConverter.ToUInt64(buffer, offset + 0x12); 39 | Unknown = BigEndianConverter.ToUInt16(buffer, offset + 0x1A); 40 | CopyNumber = BigEndianConverter.ToUInt16(buffer, offset + 0x1C); 41 | } 42 | 43 | public void WriteBytes(byte[] buffer, int offset) 44 | { 45 | ByteWriter.WriteAnsiString(buffer, offset + 0x00, Name, 8); 46 | BigEndianWriter.WriteUInt16(buffer, offset + 0x08, (ushort)RegionFlags); 47 | BigEndianWriter.WriteUInt64(buffer, offset + 0x0A, StartLBA); 48 | BigEndianWriter.WriteUInt64(buffer, offset + 0x12, SizeLBA); 49 | BigEndianWriter.WriteUInt16(buffer, offset + 0x1A, Unknown); 50 | BigEndianWriter.WriteUInt16(buffer, offset + 0x1C, CopyNumber); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/VolumeManagerDatabaseCopy.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace DiskAccessLibrary.LogicalDiskManager 10 | { 11 | /// 12 | /// A Volume Manager Database copy stored on a specific disk 13 | /// 14 | public class VolumeManagerDatabaseCopy : VolumeManagerDatabase 15 | { 16 | private DynamicDisk m_disk; 17 | private VolumeManagerDatabaseHeader m_databaseHeader; 18 | private List m_databaseRecords; 19 | private KernelUpdateLog m_kernelUpdateLog; 20 | 21 | public VolumeManagerDatabaseCopy(DynamicDisk disk, VolumeManagerDatabaseHeader databaseHeader, List databaseRecords, KernelUpdateLog kernelUpdateLog) : 22 | base(databaseHeader, databaseRecords, kernelUpdateLog) 23 | { 24 | m_disk = disk; 25 | m_databaseHeader = databaseHeader; 26 | m_databaseRecords = databaseRecords; 27 | m_kernelUpdateLog = kernelUpdateLog; 28 | } 29 | 30 | public override void WriteDatabaseHeader() 31 | { 32 | VolumeManagerDatabaseHeader.WriteToDisk(m_disk, m_databaseHeader); 33 | } 34 | 35 | public override void WriteDatabaseRecordFragment(DatabaseRecordFragment fragment) 36 | { 37 | WriteDatabaseRecordFragment(m_disk, fragment, (int)m_databaseHeader.BlockSize); 38 | } 39 | 40 | public override void SetKernelUpdateLogLastEntry(ulong committedTransactionID, ulong pendingTransactionID) 41 | { 42 | m_kernelUpdateLog.SetLastEntry(m_disk, committedTransactionID, pendingTransactionID); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Volumes/SimpleVolume.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | public class SimpleVolume : DynamicVolume 13 | { 14 | DynamicDiskExtent m_extent; 15 | 16 | public SimpleVolume(DynamicDiskExtent extent, Guid volumeGuid, Guid diskGroupGuid) : base(volumeGuid, diskGroupGuid) 17 | { 18 | m_extent = extent; 19 | } 20 | 21 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 22 | { 23 | return m_extent.ReadSectors(sectorIndex, sectorCount); 24 | } 25 | 26 | public override void WriteSectors(long sectorIndex, byte[] data) 27 | { 28 | m_extent.WriteSectors(sectorIndex, data); 29 | } 30 | 31 | public override int BytesPerSector 32 | { 33 | get 34 | { 35 | return m_extent.BytesPerSector; 36 | } 37 | } 38 | 39 | public override long Size 40 | { 41 | get 42 | { 43 | return m_extent.Size; 44 | } 45 | } 46 | 47 | public long FirstSector 48 | { 49 | get 50 | { 51 | return m_extent.FirstSector; 52 | } 53 | } 54 | 55 | public Disk Disk 56 | { 57 | get 58 | { 59 | return m_extent.Disk; 60 | } 61 | } 62 | 63 | public DynamicDiskExtent DiskExtent 64 | { 65 | get 66 | { 67 | return m_extent; 68 | } 69 | } 70 | 71 | public override List Columns 72 | { 73 | get 74 | { 75 | List extents = new List(); 76 | extents.Add(m_extent); 77 | List result = new List(); 78 | result.Add(new DynamicColumn(extents)); 79 | return result; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DiskAccessLibrary/LogicalDiskManager/Volumes/SpannedVolume.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary.LogicalDiskManager 11 | { 12 | public class SpannedVolume : DynamicVolume 13 | { 14 | private DynamicColumn m_column; 15 | 16 | public SpannedVolume(DynamicColumn column, Guid volumeGuid, Guid diskGroupGuid) : base(volumeGuid, diskGroupGuid) 17 | { 18 | m_column = column; 19 | } 20 | 21 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 22 | { 23 | CheckBoundaries(sectorIndex, sectorCount); 24 | return m_column.ReadSectors(sectorIndex, sectorCount); 25 | } 26 | 27 | public override void WriteSectors(long sectorIndex, byte[] data) 28 | { 29 | CheckBoundaries(sectorIndex, data.Length / this.BytesPerSector); 30 | m_column.WriteSectors(sectorIndex, data); 31 | } 32 | 33 | public override List Columns 34 | { 35 | get 36 | { 37 | List result = new List(); 38 | result.Add(m_column); 39 | return result; 40 | } 41 | } 42 | 43 | public override long Size 44 | { 45 | get 46 | { 47 | return m_column.Size; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/GuidPartitionTable/GuidPartitionEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Text; 9 | using Utilities; 10 | 11 | namespace DiskAccessLibrary 12 | { 13 | public class GuidPartitionEntry 14 | { 15 | // length is defined in GPT Header 16 | 17 | public Guid PartitionTypeGuid; 18 | public Guid PartitionGuid; 19 | public ulong FirstLBA; 20 | public ulong LastLBA; 21 | public ulong AttributeFlags; 22 | public string PartitionName; 23 | 24 | public int EntryIndex; // We may use this later for write operations 25 | 26 | public GuidPartitionEntry() 27 | { 28 | PartitionName = String.Empty; 29 | } 30 | 31 | public GuidPartitionEntry(byte[] buffer, int offset) 32 | { 33 | PartitionTypeGuid = LittleEndianConverter.ToGuid(buffer, offset + 0); 34 | PartitionGuid = LittleEndianConverter.ToGuid(buffer, offset + 16); 35 | FirstLBA = LittleEndianConverter.ToUInt64(buffer, offset + 32); 36 | LastLBA = LittleEndianConverter.ToUInt64(buffer, offset + 40); 37 | AttributeFlags = LittleEndianConverter.ToUInt64(buffer, offset + 48); 38 | PartitionName = UnicodeEncoding.Unicode.GetString(ByteReader.ReadBytes(buffer, offset + 56, 72)).TrimEnd('\0'); 39 | } 40 | 41 | public void WriteBytes(byte[] buffer, int offset) 42 | { 43 | LittleEndianWriter.WriteGuid(buffer, offset + 0, PartitionTypeGuid); 44 | LittleEndianWriter.WriteGuid(buffer, offset + 16, PartitionGuid); 45 | LittleEndianWriter.WriteUInt64(buffer, offset + 32, FirstLBA); 46 | LittleEndianWriter.WriteUInt64(buffer, offset + 40, LastLBA); 47 | LittleEndianWriter.WriteUInt64(buffer, offset + 48, AttributeFlags); 48 | while (PartitionName.Length < 36) 49 | { 50 | PartitionName += "\0"; 51 | } 52 | ByteWriter.WriteUTF16String(buffer, offset + 56, PartitionName, 36); 53 | } 54 | 55 | public ulong SizeLBA 56 | { 57 | get 58 | { 59 | return LastLBA - FirstLBA + 1; 60 | } 61 | } 62 | 63 | public static void WriteToDisk(Disk disk, GuidPartitionTableHeader header, GuidPartitionEntry entry) 64 | { 65 | long sectorIndex = (long)header.PartitionEntriesLBA + entry.EntryIndex * header.SizeOfPartitionEntry / disk.BytesPerSector; 66 | int entriesPerSector = (int)(disk.BytesPerSector / header.SizeOfPartitionEntry); 67 | int indexInSector = (int)(entry.EntryIndex % entriesPerSector); 68 | byte[] buffer = disk.ReadSector(sectorIndex); 69 | entry.WriteBytes(buffer, indexInSector * (int)header.SizeOfPartitionEntry); 70 | disk.WriteSectors(sectorIndex, buffer); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/GuidPartitionTable/GuidPartitionEntryCollection.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace DiskAccessLibrary 11 | { 12 | public class GuidPartitionEntryCollection 13 | { 14 | public static int GetIndexOfPartitionGuid(List entries, Guid partitionGuid) 15 | { 16 | for (int index = 0; index < entries.Count; index++) 17 | { 18 | if (entries[index].PartitionGuid.Equals(partitionGuid)) 19 | { 20 | return index; 21 | } 22 | } 23 | return -1; 24 | } 25 | 26 | public static bool ContainsPartitionTypeGuid(List entries, Guid typeGuid) 27 | { 28 | int index = GetIndexOfPartitionTypeGuid(entries, typeGuid); 29 | return (index >= 0); 30 | } 31 | 32 | public static int GetIndexOfPartitionTypeGuid(List entries, Guid typeGuid) 33 | { 34 | for (int index = 0; index < entries.Count; index++) 35 | { 36 | if (entries[index].PartitionTypeGuid.Equals(typeGuid)) 37 | { 38 | return index; 39 | } 40 | } 41 | return -1; 42 | } 43 | 44 | public static byte[] GetBytes(GuidPartitionTableHeader header, List entries) 45 | { 46 | byte[] buffer = new byte[header.NumberOfPartitionEntries * header.SizeOfPartitionEntry]; 47 | int count = (int)Math.Min(header.NumberOfPartitionEntries, entries.Count); 48 | for (int index = 0; index < count; index++) 49 | { 50 | entries[index].WriteBytes(buffer, index * (int)header.SizeOfPartitionEntry); 51 | } 52 | return buffer; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/MasterBootRecord/CHSAddress.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalAloni/DiskAccessLibrary/f32da3743faacf412ac6e540178afaacbd2abe80/DiskAccessLibrary/PartitionTables/MasterBootRecord/CHSAddress.cs -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/MasterBootRecord/MasterBootRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using Utilities; 11 | 12 | namespace DiskAccessLibrary 13 | { 14 | public class MasterBootRecord 15 | { 16 | public const ushort MBRSignature = 0xAA55; 17 | public const int NumberOfPartitionEntries = 4; 18 | 19 | public byte[] Code = new byte[440]; 20 | public uint DiskSignature; 21 | public PartitionTableEntry[] PartitionTable = new PartitionTableEntry[4]; 22 | // ushort Signature; 23 | 24 | public MasterBootRecord() 25 | { 26 | for (int index = 0; index < NumberOfPartitionEntries; index++) 27 | { 28 | PartitionTable[index] = new PartitionTableEntry(); 29 | } 30 | } 31 | 32 | public MasterBootRecord(byte[] buffer) 33 | { 34 | Array.Copy(buffer, Code, 440); 35 | DiskSignature = LittleEndianConverter.ToUInt32(buffer, 440); 36 | int offset = 446; 37 | for (int index = 0; index < NumberOfPartitionEntries; index++) 38 | { 39 | PartitionTable[index] = new PartitionTableEntry(buffer, offset); 40 | offset += 16; 41 | } 42 | ushort signature = LittleEndianConverter.ToUInt16(buffer, 510); 43 | if (signature != MBRSignature) 44 | { 45 | throw new InvalidDataException("Invalid MBR signature"); 46 | } 47 | } 48 | 49 | public byte[] GetBytes(int sectorSize) 50 | { 51 | byte[] buffer = new byte[sectorSize]; 52 | ByteWriter.WriteBytes(buffer, 0, Code, Math.Min(Code.Length, 440)); 53 | LittleEndianWriter.WriteUInt32(buffer, 440, DiskSignature); 54 | 55 | int offset = 446; 56 | for (int index = 0; index < NumberOfPartitionEntries; index++) 57 | { 58 | PartitionTable[index].WriteBytes(buffer, offset); 59 | offset += PartitionTableEntry.Length; 60 | } 61 | 62 | LittleEndianWriter.WriteUInt16(buffer, 510, MBRSignature); 63 | 64 | return buffer; 65 | } 66 | 67 | public bool IsGPTBasedDisk 68 | { 69 | get 70 | { 71 | return (PartitionTable[0].PartitionTypeName == PartitionTypeName.EFIGPT); 72 | } 73 | } 74 | 75 | public static MasterBootRecord ReadFromDisk(Disk disk) 76 | { 77 | byte[] buffer = disk.ReadSector(0); 78 | ushort signature = LittleEndianConverter.ToUInt16(buffer, 510); 79 | if (signature == MBRSignature) 80 | { 81 | return new MasterBootRecord(buffer); 82 | } 83 | else 84 | { 85 | return null; 86 | } 87 | } 88 | 89 | public static void WriteToDisk(Disk disk, MasterBootRecord mbr) 90 | { 91 | byte[] buffer = mbr.GetBytes(disk.BytesPerSector); 92 | disk.WriteSectors(0, buffer); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/MasterBootRecord/PartitionTableEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace DiskAccessLibrary 13 | { 14 | public class PartitionTableEntry 15 | { 16 | public const int Length = 16; 17 | 18 | public byte Status; 19 | public CHSAddress FirstSectorCHS; 20 | public byte PartitionType; 21 | public CHSAddress LastSectorCHS; 22 | public uint FirstSectorLBA; 23 | public uint SectorCountLBA; 24 | 25 | public PartitionTableEntry() 26 | { 27 | FirstSectorCHS = new CHSAddress(); 28 | LastSectorCHS = new CHSAddress(); 29 | } 30 | 31 | public PartitionTableEntry(byte[] buffer, int offset) 32 | { 33 | Status = buffer[offset + 0x00]; 34 | FirstSectorCHS = new CHSAddress(buffer, offset + 0x01); 35 | PartitionType = buffer[offset + 0x04]; 36 | LastSectorCHS = new CHSAddress(buffer, offset + 0x05); 37 | FirstSectorLBA = LittleEndianConverter.ToUInt32(buffer, offset + 0x08); 38 | SectorCountLBA = LittleEndianConverter.ToUInt32(buffer, offset + 0x0C); 39 | } 40 | 41 | public void WriteBytes(byte[] buffer, int offset) 42 | { 43 | buffer[offset + 0x00] = Status; 44 | FirstSectorCHS.WriteBytes(buffer, offset + 0x01); 45 | buffer[offset + 0x04] = PartitionType; 46 | LastSectorCHS.WriteBytes(buffer, offset + 0x05); 47 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x08, FirstSectorLBA); 48 | LittleEndianWriter.WriteUInt32(buffer, offset + 0x0C, SectorCountLBA); 49 | } 50 | 51 | public PartitionTypeName PartitionTypeName 52 | { 53 | get 54 | { 55 | return (PartitionTypeName)PartitionType; 56 | } 57 | set 58 | { 59 | PartitionType = (byte)value; 60 | } 61 | } 62 | 63 | public bool IsBootable 64 | { 65 | get 66 | { 67 | return (Status == 0x80); 68 | } 69 | set 70 | { 71 | Status |= 0x80; 72 | } 73 | } 74 | 75 | public bool IsValid 76 | { 77 | get 78 | { 79 | return (Status == 0x80 || Status == 0x00); 80 | } 81 | } 82 | 83 | public uint LastSectorLBA 84 | { 85 | get 86 | { 87 | return this.FirstSectorLBA + this.SectorCountLBA - 1; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /DiskAccessLibrary/PartitionTables/MasterBootRecord/PartitionTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary 3 | { 4 | public enum PartitionTypeName : byte 5 | { 6 | Primary = 0x07, 7 | Extended = 0x05, 8 | DynamicData = 0x42, 9 | EFIGPT = 0xEE, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Volumes/MBRPartition.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2025 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary 9 | { 10 | public class MBRPartition : Partition 11 | { 12 | private byte m_partitionType; 13 | 14 | public MBRPartition(byte partitionType, DiskExtent extent) : base(extent) 15 | { 16 | m_partitionType = partitionType; 17 | } 18 | 19 | public MBRPartition(byte partitionType, Disk disk, long firstSector, long size) : base(disk, firstSector, size) 20 | { 21 | m_partitionType = partitionType; 22 | } 23 | 24 | public byte PartitionType 25 | { 26 | get 27 | { 28 | return m_partitionType; 29 | } 30 | } 31 | 32 | public PartitionTypeName PartitionTypeName 33 | { 34 | get 35 | { 36 | return (PartitionTypeName)m_partitionType; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Volumes/Partition.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2018 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace DiskAccessLibrary 10 | { 11 | public abstract class Partition : Volume 12 | { 13 | private DiskExtent m_extent; 14 | 15 | public Partition(DiskExtent extent) 16 | { 17 | m_extent = extent; 18 | } 19 | 20 | public Partition(Disk disk, long firstSector, long size) 21 | { 22 | m_extent = new DiskExtent(disk, firstSector, size); 23 | } 24 | 25 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 26 | { 27 | return m_extent.ReadSectors(sectorIndex, sectorCount); 28 | } 29 | 30 | public override void WriteSectors(long sectorIndex, byte[] data) 31 | { 32 | m_extent.WriteSectors(sectorIndex, data); 33 | } 34 | 35 | public override int BytesPerSector 36 | { 37 | get 38 | { 39 | return m_extent.BytesPerSector; 40 | } 41 | } 42 | 43 | public override long Size 44 | { 45 | get 46 | { 47 | return m_extent.Size; 48 | } 49 | } 50 | 51 | public override bool IsReadOnly 52 | { 53 | get 54 | { 55 | return m_extent.IsReadOnly; 56 | } 57 | } 58 | 59 | public DiskExtent Extent 60 | { 61 | get 62 | { 63 | return m_extent; 64 | } 65 | } 66 | 67 | public override List Extents 68 | { 69 | get 70 | { 71 | List result = new List(); 72 | result.Add(m_extent); 73 | return result; 74 | } 75 | } 76 | 77 | public Disk Disk 78 | { 79 | get 80 | { 81 | return m_extent.Disk; 82 | } 83 | } 84 | 85 | public long FirstSector 86 | { 87 | get 88 | { 89 | return m_extent.FirstSector; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Volumes/RemovableVolume.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace DiskAccessLibrary 9 | { 10 | /// 11 | /// A single volume that takes up all of the disk (there is no MBR), 12 | /// The disk has no partition table and the first sector contains the filesystem boot record. 13 | /// 14 | /// MSDN definition: 15 | /// A super floppy layout is one in which there is no MBR, so there is no partition table. The entire disk (from start to end) is one giant partition. 16 | /// 17 | public class RemovableVolume : Partition 18 | { 19 | public RemovableVolume(DiskExtent extent) : base(extent) 20 | { 21 | } 22 | 23 | public RemovableVolume(Disk disk) : base(disk, 0, disk.Size) 24 | { 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Win32/Enums/Win32Error.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace DiskAccessLibrary 3 | { 4 | /// 5 | /// Win32 error codes: 6 | /// 7 | /// All Win32 error codes MUST be in the range 0x0000 to 0xFFFF 8 | public enum Win32Error : ushort 9 | { 10 | ERROR_SUCCESS = 0x0000, 11 | ERROR_INVALID_FUNCTION = 0x0001, 12 | ERROR_FILE_NOT_FOUND = 0x0002, 13 | ERROR_ACCESS_DENIED = 0x0005, 14 | ERROR_INVALID_DATA = 0x000D, 15 | ERROR_NOT_READY = 0x0015, 16 | ERROR_SECTOR_NOT_FOUND = 0x001B, 17 | ERROR_CRC = 0x0017, // This is the same error as STATUS_DEVICE_DATA_ERROR, and it means the disk has a bad block 18 | ERROR_SHARING_VIOLATION = 0x0020, 19 | ERROR_DISK_FULL = 0x0070, 20 | ERROR_INSUFFICIENT_BUFFER = 0x007A, 21 | ERROR_INVALID_NAME = 0x007B, 22 | ERROR_DIR_NOT_EMPTY = 0x0091, 23 | ERROR_BAD_PATHNAME = 0x00A1, 24 | ERROR_ALREADY_EXISTS = 0x00B7, 25 | ERROR_MORE_DATA = 0x00EA, // buffer was not long enough 26 | ERROR_NO_MORE_ITEMS = 0x0103, 27 | ERROR_IO_PENDING = 0x3E5, 28 | ERROR_MEDIA_CHANGED = 0x0456, 29 | ERROR_NO_MEDIA_IN_DRIVE = 0x0458, 30 | ERROR_IO_DEVICE = 0x045D, // Reading from disk region that has sectors with mismatching CRC may return this 31 | ERROR_DEVICE_NOT_CONNECTED = 0x048F, 32 | ERROR_NO_SYSTEM_RESOURCES = 0x05AA, // Occurs when we try to read too many bytes at once 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Win32/Helpers/IOExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | using System.Reflection; 10 | 11 | namespace DiskAccessLibrary 12 | { 13 | public class IOExceptionHelper 14 | { 15 | public static ushort GetWin32ErrorCode(IOException ex) 16 | { 17 | int hResult = GetExceptionHResult(ex); 18 | // The Win32 error code is stored in the 16 first bits of the value 19 | return (ushort)(hResult & 0x0000FFFF); 20 | } 21 | 22 | public static int GetExceptionHResult(IOException ex) 23 | { 24 | PropertyInfo hResult = ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 25 | return (int)hResult.GetValue(ex, null); 26 | } 27 | 28 | public static int GetHResultFromWin32Error(Win32Error error) 29 | { 30 | if (error == Win32Error.ERROR_SUCCESS) 31 | { 32 | return 0; 33 | } 34 | else 35 | { 36 | return (int)(0x80070000 | (ushort)error); 37 | } 38 | } 39 | 40 | /// The Win32 error code associated with the exception to throw 41 | public static void ThrowIOError(int win32ErrorCode, string defaultMessage) 42 | { 43 | if (win32ErrorCode == (int)Win32Error.ERROR_ACCESS_DENIED) 44 | { 45 | // UnauthorizedAccessException will be thrown if stream was opened only for writing or if a user is not an administrator 46 | throw new UnauthorizedAccessException(defaultMessage); 47 | } 48 | else if (win32ErrorCode == (int)Win32Error.ERROR_SHARING_VIOLATION) 49 | { 50 | throw new SharingViolationException(defaultMessage); 51 | } 52 | else if (win32ErrorCode == (int)Win32Error.ERROR_SECTOR_NOT_FOUND) 53 | { 54 | string message = defaultMessage + " The sector does not exist."; 55 | int hresult = GetHResultFromWin32Error((Win32Error)win32ErrorCode); 56 | throw new IOException(message, hresult); 57 | } 58 | else if (win32ErrorCode == (int)Win32Error.ERROR_CRC) 59 | { 60 | string message = defaultMessage + " Data Error (Cyclic Redundancy Check)."; 61 | throw new CyclicRedundancyCheckException(message); 62 | } 63 | else if (win32ErrorCode == (int)Win32Error.ERROR_NO_SYSTEM_RESOURCES) 64 | { 65 | throw new OutOfMemoryException(); 66 | } 67 | else 68 | { 69 | string message = defaultMessage + String.Format(" Win32 Error: {0}", win32ErrorCode); 70 | int hresult = GetHResultFromWin32Error((Win32Error)win32ErrorCode); 71 | throw new IOException(message, hresult); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /DiskAccessLibrary/Win32/Utilities/FileStreamUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using Microsoft.Win32.SafeHandles; 13 | 14 | namespace DiskAccessLibrary 15 | { 16 | public class FileStreamUtils 17 | { 18 | [DllImport("kernel32.dll", SetLastError = true)] 19 | private static extern bool SetFileValidData(SafeFileHandle handle, long validDataLength); 20 | 21 | /// 22 | /// On NTFS, extending a file reserves disk space but does not zero out the data. 23 | /// Instead, NTFS keeps track of the "last byte written", technically known as the valid data length, and only zeroes out up to that point. 24 | /// The data past the valid data length are logically zero but are not physically zero on disk. 25 | /// When you write to a point past the current valid data length, all the bytes between the valid data length and the start of your write need to be zeroed out before the new valid data length can be set to the end of your write operation. 26 | /// Extending the file and then calling SetValidLength() may save a considerable amount of time zeroing out the extended portion of the file. 27 | /// 28 | /// 29 | /// Calling SetFileValidData requires SeManageVolumePrivilege privileges. 30 | /// 31 | public static bool SetValidLength(FileStream fileStream, long validDataLength) 32 | { 33 | bool success = SetFileValidData(fileStream.SafeFileHandle, validDataLength); 34 | if (!success) 35 | { 36 | int errorCode = Marshal.GetLastWin32Error(); 37 | string message = String.Format("Unable to set valid file length, Win32 Error: {0}", errorCode); 38 | throw new IOException(message); 39 | } 40 | return success; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Publish.bat: -------------------------------------------------------------------------------- 1 | dotnet pack DiskAccessLibrary/DiskAccessLibrary.csproj -c Publish -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | DiskAccessLibrary: 2 | =================== 3 | DiskAccessLibrary is an open-source C# library allowing access to physical and virtual disks (IMG/VHD/VMDK) including reading and writing various on-disk structutes (MBR/GPT, Logical Disk Manager Database) and filesystems (NTFS). 4 | 5 | ##### Q: What this library can do? 6 | 1. Create IMG/VHD/VMDK virtual hard drives. 7 | 2. Read and write from IMG/VHD/VMDK virtual hard drives. 8 | 3. Read and modify MBR and GPT partition table. 9 | 4. Read and modify Windows Logical Disk manager database. 10 | 5. Read files from NTFS formatted volumes. 11 | 6. Write files to NTFS formatted volums. (The code was not updated to reflect changes in Windows Vista and later and was only validated against Windows XP and Windows Server 2003) 12 | 7. Create NTFS formatted volumes. (The code was not updated to reflect changes in Windows Vista and later and was only validated against Windows XP and Windows Server 2003) 13 | 14 | ##### Warnings: 15 | 1. The software may contain bugs and/or limitations that may result in data loss, backup your critical data before using it. 16 | I take no responsibility for any data loss that may occur. 17 | 18 | 2. There are no official specifications for the LDM database. 19 | the structure has been reverse engineered ( https://flatcap.github.io/linux-ntfs/ldm/ ) and while I believe most of the information is accurate, there are some unknowns. 20 | 21 | 3. There are no official specifications for the NTFS file system. 22 | 23 | ##### Programs using DiskAccessLibrary: 24 | [Dynamic Disk Partitioner](https://github.com/TalAloni/DynamicDiskPartitioner) 25 | [iSCSI Console](https://github.com/TalAloni/iSCSIConsole) 26 | [Hard Disk Validator](https://github.com/TalAloni/HardDiskValidator) 27 | [Raw Disk Copier](https://github.com/TalAloni/RawDiskCopier) 28 | [VmdkZeroFree](https://github.com/TalAloni/VmdkZeroFree) 29 | 30 | Contact: 31 | ======== 32 | If you have any question, feel free to contact me. 33 | Tal Aloni 34 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/ByteUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | 10 | namespace Utilities 11 | { 12 | public static class ByteUtils 13 | { 14 | public static byte[] Concatenate(byte[] a, byte[] b) 15 | { 16 | byte[] result = new byte[a.Length + b.Length]; 17 | Array.Copy(a, 0, result, 0, a.Length); 18 | Array.Copy(b, 0, result, a.Length, b.Length); 19 | return result; 20 | } 21 | 22 | public static bool AreByteArraysEqual(byte[] array1, byte[] array2) 23 | { 24 | if (array1.Length != array2.Length) 25 | { 26 | return false; 27 | } 28 | 29 | for (int index = 0; index < array1.Length; index++) 30 | { 31 | if (array1[index] != array2[index]) 32 | { 33 | return false; 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | 40 | public static byte[] XOR(byte[] array1, byte[] array2) 41 | { 42 | if (array1.Length == array2.Length) 43 | { 44 | return XOR(array1, 0, array2, 0, array1.Length); 45 | } 46 | else 47 | { 48 | throw new ArgumentException("Arrays must be of equal length"); 49 | } 50 | } 51 | 52 | public static byte[] XOR(byte[] array1, int offset1, byte[] array2, int offset2, int length) 53 | { 54 | if (offset1 + length <= array1.Length && offset2 + length <= array2.Length) 55 | { 56 | byte[] result = new byte[length]; 57 | for (int index = 0; index < length; index++) 58 | { 59 | result[index] = (byte)(array1[offset1 + index] ^ array2[offset2 + index]); 60 | } 61 | return result; 62 | } 63 | else 64 | { 65 | throw new ArgumentOutOfRangeException(); 66 | } 67 | } 68 | 69 | public static long CopyStream(Stream input, Stream output) 70 | { 71 | // input may not support seeking, so don't use input.Position 72 | return CopyStream(input, output, Int64.MaxValue); 73 | } 74 | 75 | public static long CopyStream(Stream input, Stream output, long count) 76 | { 77 | const int MaxBufferSize = 1048576; // 1 MB 78 | int bufferSize = (int)Math.Min(MaxBufferSize, count); 79 | byte[] buffer = new byte[bufferSize]; 80 | long totalBytesRead = 0; 81 | while (totalBytesRead < count) 82 | { 83 | int numberOfBytesToRead = (int)Math.Min(bufferSize, count - totalBytesRead); 84 | int bytesRead = input.Read(buffer, 0, numberOfBytesToRead); 85 | totalBytesRead += bytesRead; 86 | output.Write(buffer, 0, bytesRead); 87 | if (bytesRead == 0) // no more bytes to read from input stream 88 | { 89 | return totalBytesRead; 90 | } 91 | } 92 | return totalBytesRead; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Utilities/Comparers/ReverseComparer.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2005-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace Utilities 11 | { 12 | public class ReverseComparer : IComparer 13 | { 14 | private IComparer m_comparer; 15 | 16 | public ReverseComparer(IComparer comparer) 17 | { 18 | m_comparer = comparer; 19 | } 20 | 21 | public int Compare(T x, T y) 22 | { 23 | return m_comparer.Compare(y, x); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Utilities/Generics/BlockingQueue.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016-2025 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace Utilities 11 | { 12 | public class BlockingQueue 13 | { 14 | private Queue m_queue = new Queue(); 15 | private int m_count = 0; 16 | private bool m_stopping; 17 | 18 | public void Enqueue(T item) 19 | { 20 | if (m_stopping) 21 | { 22 | return; 23 | } 24 | 25 | lock (m_queue) 26 | { 27 | m_queue.Enqueue(item); 28 | m_count++; 29 | if (m_queue.Count == 1) 30 | { 31 | Monitor.Pulse(m_queue); 32 | } 33 | } 34 | } 35 | 36 | public void Enqueue(List items) 37 | { 38 | if (m_stopping || items.Count == 0) 39 | { 40 | return; 41 | } 42 | 43 | lock (m_queue) 44 | { 45 | foreach (T item in items) 46 | { 47 | m_queue.Enqueue(item); 48 | m_count++; 49 | } 50 | if (m_queue.Count == items.Count) 51 | { 52 | Monitor.Pulse(m_queue); 53 | } 54 | } 55 | } 56 | 57 | /// Will return false if the BlockingQueue is stopped 58 | public bool TryDequeue(out T item) 59 | { 60 | lock (m_queue) 61 | { 62 | while (m_queue.Count == 0) 63 | { 64 | if (!m_stopping) 65 | { 66 | Monitor.Wait(m_queue); 67 | } 68 | 69 | if (m_stopping && m_queue.Count == 0) 70 | { 71 | item = default(T); 72 | return false; 73 | } 74 | } 75 | 76 | item = m_queue.Dequeue(); 77 | m_count--; 78 | return true; 79 | } 80 | } 81 | 82 | public void Stop() 83 | { 84 | lock (m_queue) 85 | { 86 | m_stopping = true; 87 | Monitor.PulseAll(m_queue); 88 | } 89 | } 90 | 91 | public void Abort() 92 | { 93 | lock (m_queue) 94 | { 95 | m_queue.Clear(); 96 | Stop(); 97 | } 98 | } 99 | 100 | public int Count 101 | { 102 | get 103 | { 104 | return m_count; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Utilities/Generics/KeyValuePairList.Sort.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.ComponentModel; 9 | 10 | namespace Utilities 11 | { 12 | public partial class KeyValuePairList 13 | { 14 | new public void Sort() 15 | { 16 | this.Sort(Comparer.Default); 17 | } 18 | 19 | public void Sort(ListSortDirection sortDirection) 20 | { 21 | Sort(Comparer.Default, sortDirection); 22 | } 23 | 24 | public void Sort(IComparer comparer, ListSortDirection sortDirection) 25 | { 26 | if (sortDirection == ListSortDirection.Ascending) 27 | { 28 | Sort(comparer); 29 | } 30 | else 31 | { 32 | Sort(new ReverseComparer(comparer)); 33 | } 34 | } 35 | 36 | public void Sort(IComparer comparer) 37 | { 38 | this.Sort(delegate(KeyValuePair a, KeyValuePair b) 39 | { 40 | return comparer.Compare(a.Key, b.Key); 41 | }); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Utilities/Generics/KeyValuePairList.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace Utilities 10 | { 11 | public partial class KeyValuePairList : List> 12 | { 13 | public KeyValuePairList() 14 | { 15 | } 16 | 17 | public KeyValuePairList(List> collection) : base(collection) 18 | { 19 | } 20 | 21 | public bool ContainsKey(TKey key) 22 | { 23 | return (this.IndexOfKey(key) != -1); 24 | } 25 | 26 | public int IndexOfKey(TKey key) 27 | { 28 | for (int index = 0; index < this.Count; index++) 29 | { 30 | if (this[index].Key.Equals(key)) 31 | { 32 | return index; 33 | } 34 | } 35 | 36 | return -1; 37 | } 38 | 39 | public TValue ValueOf(TKey key) 40 | { 41 | for (int index = 0; index < this.Count; index++) 42 | { 43 | if (this[index].Key.Equals(key)) 44 | { 45 | return this[index].Value; 46 | } 47 | } 48 | 49 | return default(TValue); 50 | } 51 | 52 | public void Add(TKey key, TValue value) 53 | { 54 | this.Add(new KeyValuePair(key, value)); 55 | } 56 | 57 | public List Keys 58 | { 59 | get 60 | { 61 | List result = new List(); 62 | foreach (KeyValuePair entity in this) 63 | { 64 | result.Add(entity.Key); 65 | } 66 | return result; 67 | } 68 | } 69 | 70 | public List Values 71 | { 72 | get 73 | { 74 | List result = new List(); 75 | foreach (KeyValuePair entity in this) 76 | { 77 | result.Add(entity.Value); 78 | } 79 | return result; 80 | } 81 | } 82 | 83 | public new KeyValuePairList GetRange(int index, int count) 84 | { 85 | return new KeyValuePairList(base.GetRange(index, count)); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Utilities/Generics/Map.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * Based on: 4 | * http://stackoverflow.com/questions/10966331/two-way-bidirectional-dictionary-in-c 5 | * 6 | * You can redistribute this program and/or modify it under the terms of 7 | * the GNU Lesser Public License as published by the Free Software Foundation, 8 | * either version 3 of the License, or (at your option) any later version. 9 | */ 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Text; 13 | 14 | namespace Utilities 15 | { 16 | public class Map 17 | { 18 | private Dictionary m_forward = new Dictionary(); 19 | private Dictionary m_reverse = new Dictionary(); 20 | 21 | public Map() 22 | { 23 | m_forward = new Dictionary(); 24 | m_reverse = new Dictionary(); 25 | } 26 | 27 | public void Add(T1 key, T2 value) 28 | { 29 | m_forward.Add(key, value); 30 | m_reverse.Add(value, key); 31 | } 32 | 33 | public bool ContainsKey(T1 key) 34 | { 35 | return m_forward.ContainsKey(key); 36 | } 37 | 38 | public bool ContainsValue(T2 value) 39 | { 40 | return m_reverse.ContainsKey(value); 41 | } 42 | 43 | public bool TryGetKey(T2 value, out T1 key) 44 | { 45 | return m_reverse.TryGetValue(value, out key); 46 | } 47 | 48 | public bool TryGetValue(T1 key, out T2 value) 49 | { 50 | return m_forward.TryGetValue(key, out value); 51 | } 52 | 53 | public void RemoveKey(T1 key) 54 | { 55 | T2 value; 56 | if (m_forward.TryGetValue(key, out value)) 57 | { 58 | m_forward.Remove(key); 59 | m_reverse.Remove(value); 60 | } 61 | } 62 | 63 | public void RemoveValue(T2 value) 64 | { 65 | T1 key; 66 | if (m_reverse.TryGetValue(value, out key)) 67 | { 68 | m_forward.Remove(key); 69 | m_reverse.Remove(value); 70 | } 71 | } 72 | 73 | public T2 this[T1 key] 74 | { 75 | get 76 | { 77 | return m_forward[key]; 78 | } 79 | } 80 | 81 | public T1 GetKey(T2 value) 82 | { 83 | return m_reverse[value]; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Utilities/Generics/Reference.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace Utilities 11 | { 12 | public class Reference where T : struct 13 | { 14 | T m_value; 15 | 16 | public Reference(T value) 17 | { 18 | m_value = value; 19 | } 20 | 21 | public T Value 22 | { 23 | get { return m_value; } 24 | set { m_value = value; } 25 | } 26 | 27 | public override string ToString() 28 | { 29 | return m_value.ToString(); 30 | } 31 | 32 | public static implicit operator T(Reference wrapper) 33 | { 34 | return wrapper.Value; 35 | } 36 | 37 | public static implicit operator Reference(T value) 38 | { 39 | return new Reference(value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Utilities/Threading/CountdownLatch.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Threading; 9 | 10 | namespace Utilities 11 | { 12 | public class CountdownLatch 13 | { 14 | private int m_count; 15 | private EventWaitHandle m_waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset); 16 | 17 | public CountdownLatch() 18 | { 19 | } 20 | 21 | public void Increment() 22 | { 23 | int count = Interlocked.Increment(ref m_count); 24 | if (count == 1) 25 | { 26 | m_waitHandle.Reset(); 27 | } 28 | } 29 | 30 | public void Add(int value) 31 | { 32 | int count = Interlocked.Add(ref m_count, value); 33 | if (count == value) 34 | { 35 | m_waitHandle.Reset(); 36 | } 37 | } 38 | 39 | public void Decrement() 40 | { 41 | int count = Interlocked.Decrement(ref m_count); 42 | if (m_count == 0) 43 | { 44 | m_waitHandle.Set(); 45 | } 46 | else if (count < 0) 47 | { 48 | throw new InvalidOperationException("Count must be greater than or equal to 0"); 49 | } 50 | } 51 | 52 | public void WaitUntilZero() 53 | { 54 | m_waitHandle.WaitOne(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Utilities/Utilities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net20;net40;netstandard2.0 5 | Utilities 6 | Utilities 7 | Tal Aloni 8 | Copyright © Tal Aloni 2005-2024 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------