├── .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