示例#1
0
        /// <summary>
        /// Builds a 3-dimensional volume from the provided volume information.
        /// This method will parallelise voxel extraction per slice.
        /// </summary>
        /// <param name="volumeInformation">The volume information.</param>
        /// <param name="maxDegreeOfParallelism">The maximum degrees of parallelism when extracting voxel data from the DICOM datasets.</param>
        /// <returns>The 3-dimensional volume.</returns>
        /// <exception cref="ArgumentNullException">The provided volume information was null.</exception>
        /// <exception cref="InvalidOperationException">The decoded DICOM pixel data was not the expected length.</exception>
        public static Volume3D <short> BuildVolume(
            VolumeInformation volumeInformation,
            uint maxDegreeOfParallelism = 100)
        {
            volumeInformation = volumeInformation ?? throw new ArgumentNullException(nameof(volumeInformation));

            // Allocate the array for reading the volume data.
            var result = new Volume3D <short>(
                (int)volumeInformation.Width,
                (int)volumeInformation.Height,
                (int)volumeInformation.Depth,
                volumeInformation.VoxelWidthInMillimeters,
                volumeInformation.VoxelHeightInMillimeters,
                volumeInformation.VoxelDepthInMillimeters,
                volumeInformation.Origin,
                volumeInformation.Direction);

            Parallel.For(
                0,
                volumeInformation.Depth,
                new ParallelOptions()
            {
                MaxDegreeOfParallelism = (int)maxDegreeOfParallelism
            },
                i => WriteSlice(result, volumeInformation.GetSliceInformation((int)i), (uint)i));

            return(result);
        }
示例#2
0
        /// <summary>
        /// Gets volume information for the given volume root path.
        /// </summary>
        public static VolumeInformation GetVolumeInformation(string rootPath)
        {
            rootPath = Paths.AddTrailingSeparator(rootPath);

            using (var volumeName = new StringBuffer(initialCharCapacity: Paths.MaxPath + 1))
                using (var fileSystemName = new StringBuffer(initialCharCapacity: Paths.MaxPath + 1))
                {
                    // Documentation claims that removable (floppy/optical) drives will prompt for media when calling this API and say to
                    // set the error mode to prevent it. I can't replicate this behavior or find any documentation on when it might have
                    // changed. I'm guessing this changed in Windows 7 when they added support for setting the thread's error mode (as
                    // opposed to the entire process).
                    uint serialNumber, maxComponentLength;
                    FileSystemFeature flags;
                    if (!Direct.GetVolumeInformationW(rootPath, volumeName, volumeName.CharCapacity, out serialNumber, out maxComponentLength, out flags, fileSystemName, fileSystemName.CharCapacity))
                    {
                        throw ErrorHelper.GetIoExceptionForLastError(rootPath);
                    }

                    volumeName.SetLengthToFirstNull();
                    fileSystemName.SetLengthToFirstNull();

                    VolumeInformation info = new VolumeInformation
                    {
                        RootPathName           = rootPath,
                        VolumeName             = volumeName.ToString(),
                        VolumeSerialNumber     = serialNumber,
                        MaximumComponentLength = maxComponentLength,
                        FileSystemFlags        = flags,
                        FileSystemName         = fileSystemName.ToString()
                    };

                    return(info);
                }
        }
            internal static VolumeInformation GetVolumeInformation(string rootPath)
            {
                rootPath = Paths.AddTrailingSeparator(rootPath);

                using (var volumeName = new StringBuffer(initialMinCapacity: Paths.MaxPath + 1))
                    using (var fileSystemName = new StringBuffer(initialMinCapacity: Paths.MaxPath + 1))
                    {
                        uint serialNumber, maxComponentLength;
                        FileSystemFeature flags;
                        if (!Private.GetVolumeInformationW(rootPath, volumeName, (uint)volumeName.CharCapacity, out serialNumber, out maxComponentLength, out flags, fileSystemName, (uint)fileSystemName.CharCapacity))
                        {
                            int lastError = Marshal.GetLastWin32Error();
                            throw GetIoExceptionForError(lastError, rootPath);
                        }

                        volumeName.SetLengthToFirstNull();
                        fileSystemName.SetLengthToFirstNull();

                        VolumeInformation info = new VolumeInformation
                        {
                            RootPathName           = rootPath,
                            VolumeName             = volumeName.ToString(),
                            VolumeSerialNumber     = serialNumber,
                            MaximumComponentLength = maxComponentLength,
                            FileSystemFlags        = flags,
                            FileSystemName         = fileSystemName.ToString()
                        };

                        return(info);
                    }
            }
示例#4
0
        protected override ExitCode ExecuteFileTask()
        {
            VolumeInformation info = ExtendedFileService.GetVolumeInformation(Arguments.Target);

            Table table = Table.Create(new ColumnFormat(1, ContentVisibility.ShowAll, Justification.Right), new ColumnFormat(1));

            table.HasHeader = false;
            table.AddRow("Volume Name", info.VolumeName);
            table.AddRow("Serial Number", info.VolumeSerialNumber.ToString());
            table.AddRow("Max Component Length", info.MaximumComponentLength.ToString());
            table.AddRow("File System Name", info.FileSystemName.ToString());
            foreach (var value in Enum.GetValues(typeof(FileSystemFeature)))
            {
                FileSystemFeature feature = (FileSystemFeature)value;
                if ((feature & info.FileSystemFlags) == feature)
                {
                    table.AddRow(feature.ToString(), "true");
                }
                else
                {
                    table.AddRow(feature.ToString(), "false");
                }
            }

            this.Loggers[LoggerType.Result].Write(table);
            return(ExitCode.Success);
        }
        public void TestVolumeInformationCreateTest()
        {
            ushort highBit          = 16;
            var    expectedDimX     = 54;
            var    expectedDimY     = 64;
            var    expectedDimZ     = 50;
            var    expectedSpacingX = 3;
            var    expectedSpacingY = 5;
            var    expectedSpacingZ = 4;
            var    expectedOrigin   = new Point3D(1, 2, 3);

            var dicomDatasets = CreateValidDicomDatasetVolume(
                expectedDimX,
                expectedDimY,
                expectedDimZ,
                expectedSpacingX,
                expectedSpacingY,
                expectedSpacingZ,
                expectedOrigin,
                DicomUID.CTImageStorage,
                highBit);

            var volumeInformation = VolumeInformation.Create(dicomDatasets);

            Assert.AreEqual(expectedDimX, volumeInformation.Width);
            Assert.AreEqual(expectedDimY, volumeInformation.Height);
            Assert.AreEqual(expectedDimZ, volumeInformation.Depth);
            Assert.AreEqual(expectedSpacingX, volumeInformation.VoxelWidthInMillimeters);
            Assert.AreEqual(expectedSpacingY, volumeInformation.VoxelHeightInMillimeters);
            Assert.AreEqual(expectedSpacingZ, volumeInformation.VoxelDepthInMillimeters);
            Assert.AreEqual(0, volumeInformation.RescaleIntercept);
            Assert.AreEqual(1, volumeInformation.RescaleSlope);
            Assert.AreEqual(highBit, volumeInformation.HighBit);
            Assert.AreEqual(true, volumeInformation.SignedPixelRepresentation);
            Assert.AreEqual(expectedOrigin.X, volumeInformation.Origin.X);
            Assert.AreEqual(expectedOrigin.Y, volumeInformation.Origin.Y);
            Assert.AreEqual(expectedOrigin.Z, volumeInformation.Origin.Z);
            Assert.AreEqual(Matrix3.CreateIdentity(), volumeInformation.Direction);

            for (var i = 0; i < dicomDatasets.Length; i++)
            {
                Assert.AreEqual(i * expectedSpacingZ + expectedOrigin.Z, volumeInformation.GetSliceInformation(i).SlicePosition);
            }

            // Exception testing.
            dicomDatasets[dicomDatasets.Length - 1].AddOrUpdate(new DicomDecimalString(DicomTag.PixelSpacing, new decimal[] { 0, 0 }));
            Assert.Throws <ArgumentException>(() => VolumeInformation.Create(dicomDatasets.Take(1)));
            var sliceInformation = new SliceInformation[1];

            Assert.Throws <ArgumentException>(() => VolumeInformation.Create(sliceInformation));
            sliceInformation = null;
            Assert.Throws <ArgumentNullException>(() => VolumeInformation.Create(sliceInformation));
            dicomDatasets = null;
            Assert.Throws <ArgumentNullException>(() => VolumeInformation.Create(dicomDatasets));
        }
示例#6
0
        static void Main(string[] args)
        {
            if (0 == args.Length)
            {
                string executableName  = AssemblyHelper.ExecutableName;
                string applicationName = Path.GetFileNameWithoutExtension(executableName);
                Console.WriteLine("{0} [UNC-path|Drive]...", applicationName);
                Console.WriteLine(@"Example: {0} C: \\{1}\Users", applicationName, Environment.MachineName);
            }
            else
            {
                string        header = string.Empty;
                StringBuilder result = new StringBuilder();
                foreach (string arg in args)
                {
                    try
                    {
                        DiskFreeSpaceEx   diskFreeSpaceEx   = DiskInfo.GetDiskFreeSpaceEx(arg);
                        VolumeInformation volumeInformation = DiskInfo.GetVolumeInformation(arg);

                        var values = new
                        {
                            diskFreeSpaceEx.DirectoryName,
                            diskFreeSpaceEx.FreeBytesAvailable,
                            diskFreeSpaceEx.TotalNumberOfBytes,
                            diskFreeSpaceEx.TotalNumberOfFreeBytes,
                            volumeInformation.VolumeSerialNumber,
                            volumeInformation.MaximumComponentLength,
                            volumeInformation.FileSystemFlags,
                            volumeInformation.FileSystemName,
                            volumeInformation.VolumeName,
                        };
                        IList <string> valueStrings = Reflector.GetValueStrings(values);

                        const string separator = ";";

                        string line = string.Join(separator, valueStrings);
                        result.AppendLine(line);

                        IList <string> names = Reflector.GetNames(values);
                        header = string.Join(separator, names);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Exception processing '{0}': {1}", arg, ex);
                    }
                }
                if (result.Length > 0)
                {
                    Console.WriteLine(header);
                    Console.Write(result);
                }
            }
        }
示例#7
0
        /// <summary>
        /// Checks the slice spacing conformance based on the acceptance test.
        /// </summary>
        /// <param name="volumeInformation">The volume information.</param>
        /// <param name="acceptanceTest">The acceptance test.</param>
        /// <exception cref="ArgumentNullException">The volume information or acceptance test was null.</exception>
        /// <exception cref="ArgumentException">The acceptance test did not pass for a slice.</exception>
        private static void CheckSliceSpacingConformance(VolumeInformation volumeInformation, IVolumeGeometricAcceptanceTest acceptanceTest)
        {
            for (var i = 1; i < volumeInformation.Depth; i++)
            {
                var spacing = volumeInformation.GetSliceInformation(i).SlicePosition - volumeInformation.GetSliceInformation(i - 1).SlicePosition;

                if (!acceptanceTest.AcceptSliceSpacingError(volumeInformation.SopClass, spacing, volumeInformation.VoxelDepthInMillimeters))
                {
                    throw new ArgumentException($"The spacing between slice {i - 1} and {i} was inconsistent.");
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        protected override void ProcessRecord()
        {
            switch (ParameterSetName)
            {
            case "ByVolume":
                WriteObject(VolumeInformation.Get(volume));
                break;

            case "ByPath":
                WriteObject(VolumeInformation.GetByPath(path));
                break;
            }
        }
        public void SystemDirTest()
        {
            VolumeInformation container = new VolumeInformation(new DirectoryInfo(Environment.SystemDirectory));

            Assert.Equal(_isCaseSensitive, container.IsCaseSensitive);
            Assert.Equal(_fileSystemFlags, container.Flags);
            Assert.Equal(_fileSystemName, container.FileSystemName);
            Assert.Equal(_maximumComponentLength, container.MaxNameLength);
            Assert.Equal(_rootDirectory.FullName, container.RootPathName);
            Assert.Equal(_rootDirectory.Name, container.Name);
            Assert.Equal(_volumeName, container.VolumeName);
            Assert.Equal(_volumeSerialNumber, container.SerialNumber);
        }
        /// <summary>
        /// Attempt to construct a 3-dimensional volume instance from the provided set of DICOM series files.
        /// </summary>
        /// <param name="dicomDatasets">The collection of DICOM datasets.</param>
        /// <param name="acceptanceTest">An implmentation of IVolumeGeometricAcceptanceTest expressing the geometric tollerances required by your application</param>
        /// <param name="supportLossyCodecs">true if it is appropriate for your application to support lossy pixel encodings</param>
        /// <returns>The created 3-dimensional volume.</returns>
        /// <exception cref="ArgumentNullException">The DICOM datasets or acceptance test is null.</exception>
        /// <exception cref="ArgumentException">A volume could not be formed from the provided DICOM series datasets.</exception>
        public static Volume3D <short> BuildVolume(
            IEnumerable <DicomDataset> dicomDatasets,
            IVolumeGeometricAcceptanceTest acceptanceTest,
            bool supportLossyCodecs)
        {
            dicomDatasets  = dicomDatasets ?? throw new ArgumentNullException(nameof(dicomDatasets));
            acceptanceTest = acceptanceTest ?? throw new ArgumentNullException(nameof(acceptanceTest));

            // 1. Construct the volume information: this requires a minimum set of DICOM tags in each dataset.
            var volumeInformation = VolumeInformation.Create(dicomDatasets);

            // 2. Now validate the volume based on the acceptance tests (will throw argument exception on failure).
            DicomSeriesInformationValidator.ValidateVolumeInformation(volumeInformation, acceptanceTest, supportLossyCodecs ? null : SupportedTransferSyntaxes);

            // 3. Now validated, lets extract the pixels as a short array.
            return(DicomSeriesImageReader.BuildVolume(volumeInformation));
        }
        public VolumeInformationTest(ITestOutputHelper outputHelper)
        {
            StringBuilder fileSystemNameBuffer = new StringBuilder(VolumeInformation.InteropStringCapacity);
            StringBuilder volumeNameBuffer     = new StringBuilder(VolumeInformation.InteropStringCapacity);

            _rootDirectory = (new DirectoryInfo(Environment.SystemDirectory)).Root;
            if (!VolumeInformation.GetVolumeInformation(_rootDirectory.FullName, volumeNameBuffer, VolumeInformation.InteropStringCapacity, out uint volumeSerialNumber, out uint maximumComponentLength, out FileSystemFeature fileSystemFlags, fileSystemNameBuffer, VolumeInformation.InteropStringCapacity))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }
            _fileSystemFlags        = fileSystemFlags;
            _isCaseSensitive        = fileSystemFlags.HasFlag(FileSystemFeature.CaseSensitiveSearch);
            _volumeSerialNumber     = volumeSerialNumber;
            _maximumComponentLength = maximumComponentLength;
            _fileSystemName         = fileSystemNameBuffer.ToString();
            _volumeName             = volumeNameBuffer.ToString();
        }
        public void TestVolumeInformationValidation()
        {
            var dicomDatasets  = CreateValidDicomDatasetVolume(5, 5, 5, 1, 1, 3, new Point3D(), DicomUID.CTImageStorage, 16);
            var acceptanceTest = new ModerateGeometricAcceptanceTest("Blah1", "Blah2");

            // Valid DICOM slice.
            DicomSeriesInformationValidator.ValidateVolumeInformation(
                VolumeInformation.Create(dicomDatasets),
                acceptanceTest,
                new[] { dicomDatasets[0].InternalTransferSyntax });

            // Inconsistent slice information.
            var dicomDatasets2 = CreateValidDicomDatasetVolume(5, 5, 5, 1, 1, 3, new Point3D(), DicomUID.CTImageStorage, 16);

            dicomDatasets2[dicomDatasets2.Length - 2].AddOrUpdate(new DicomUnsignedShort(DicomTag.Rows, 234));
            var exception = Assert.Throws <ArgumentException>(() => DicomSeriesInformationValidator.ValidateVolumeInformation(
                                                                  VolumeInformation.Create(dicomDatasets2),
                                                                  acceptanceTest));

            Assert.IsTrue(exception.Message.Contains("Slice at position '9' has an inconsistent height. Expected: '5', Actual: '234'."));

            // Invalid supported transfer syntax
            Assert.Throws <ArgumentException>(() => DicomSeriesInformationValidator.ValidateVolumeInformation(
                                                  VolumeInformation.Create(dicomDatasets),
                                                  acceptanceTest,
                                                  new[] { DicomTransferSyntax.DeflatedExplicitVRLittleEndian }));

            // Failing acceptance test
            Assert.Throws <ArgumentException>(() => DicomSeriesInformationValidator.ValidateVolumeInformation(
                                                  VolumeInformation.Create(dicomDatasets),
                                                  new FailingAcceptanceTest()));

            // Exception testing
            Assert.Throws <ArgumentNullException>(() => DicomSeriesInformationValidator.ValidateVolumeInformation(
                                                      VolumeInformation.Create(dicomDatasets),
                                                      null));

            Assert.Throws <ArgumentNullException>(() => DicomSeriesInformationValidator.ValidateVolumeInformation(
                                                      null,
                                                      acceptanceTest));
        }
示例#13
0
        /// <summary>
        /// Validates the provided volume information in accordance with the provided volume geometric acceptance test and
        /// that every slice in the volume is valid.
        /// This will check:
        ///     1. Validates each slice using the validate slice information method (and will use the supported transfer syntaxes if provided).
        ///     2. Grid conformance of the volume.
        ///     3. Slice spacing conformance for each slice.
        ///     4. Executes the propose method of the acceptance test.
        /// </summary>
        /// <param name="volumeInformation">The volume information.</param>
        /// <param name="volumeGeometricAcceptanceTest">The volume geometric acceptance test.</param>
        /// <param name="supportedTransferSyntaxes">The supported transfer syntaxes or null if we do not want to check against this.</param>
        /// <exception cref="ArgumentNullException">The volume information or acceptance test is null.</exception>
        /// <exception cref="ArgumentException">An acceptance test did not pass.</exception>
        public static void ValidateVolumeInformation(
            VolumeInformation volumeInformation,
            IVolumeGeometricAcceptanceTest volumeGeometricAcceptanceTest,
            IReadOnlyCollection <DicomTransferSyntax> supportedTransferSyntaxes = null)
        {
            volumeInformation             = volumeInformation ?? throw new ArgumentNullException(nameof(volumeInformation));
            volumeGeometricAcceptanceTest = volumeGeometricAcceptanceTest ?? throw new ArgumentNullException(nameof(volumeGeometricAcceptanceTest));

            // 1. Validate each slice.
            for (var i = 0; i < volumeInformation.Depth; i++)
            {
                // Validate the DICOM tags of each slice.
                ValidateSliceInformation(volumeInformation.GetSliceInformation(i), supportedTransferSyntaxes);

                if (i > 0)
                {
                    // Validate the slice information is consistent across slices using the first slice as a reference.
                    ValidateSliceInformation(volumeInformation.GetSliceInformation(i), volumeInformation.GetSliceInformation(0));
                }
            }

            // 2. + 3. Check the slice and grid conformance of the volume information.
            CheckGridConformance(volumeInformation, volumeGeometricAcceptanceTest);
            CheckSliceSpacingConformance(volumeInformation, volumeGeometricAcceptanceTest);

            // 4. Run acceptance testing.
            var acceptanceErrorMessage = string.Empty;

            if (!volumeGeometricAcceptanceTest.Propose(
                    volumeInformation.SopClass,
                    volumeInformation.Origin,
                    volumeInformation.Direction,
                    new Point3D(volumeInformation.VoxelWidthInMillimeters, volumeInformation.VoxelHeightInMillimeters, volumeInformation.Depth),
                    out acceptanceErrorMessage))
            {
                throw new ArgumentException(acceptanceErrorMessage, nameof(volumeInformation));
            }
        }
示例#14
0
        /// <summary>
        /// Checks the grid conformance of the provided volume information based on the provided geometric acceptance test.
        /// </summary>
        /// <param name="volumeInformation">The volume information.</param>
        /// <param name="acceptanceTest">The acceptance test.</param>
        /// <exception cref="ArgumentNullException">The volume information or acceptance test was null.</exception>
        /// <exception cref="ArgumentException">The series did not conform to a regular grid.</exception>
        private static void CheckGridConformance(VolumeInformation volumeInformation, IVolumeGeometricAcceptanceTest acceptanceTest)
        {
            volumeInformation = volumeInformation ?? throw new ArgumentNullException(nameof(volumeInformation));
            acceptanceTest    = acceptanceTest ?? throw new ArgumentNullException(nameof(acceptanceTest));

            var scales = Matrix3.Diag(
                volumeInformation.VoxelWidthInMillimeters,
                volumeInformation.VoxelHeightInMillimeters,
                volumeInformation.VoxelDepthInMillimeters);

            if (volumeInformation.Depth != volumeInformation.Depth)
            {
                throw new ArgumentException("Mismatch between depth and number of slices.", nameof(volumeInformation));
            }

            for (int z = 0; z < volumeInformation.Depth; z++)
            {
                var sliceInformation = volumeInformation.GetSliceInformation(z);
                var sliceScales      = Matrix3.Diag(sliceInformation.VoxelWidthInMillimeters, sliceInformation.VoxelHeightInMillimeters, 0);
                var sliceOrientation = Matrix3.FromColumns(sliceInformation.Direction.Column(0), sliceInformation.Direction.Column(1), new Point3D(0, 0, 0));

                // Check corners of each slice only
                for (uint y = 0; y < sliceInformation.Height; y += sliceInformation.Height - 1)
                {
                    for (uint x = 0; x < sliceInformation.Width; x += sliceInformation.Width - 1)
                    {
                        var point = new Point3D(x, y, z);
                        var patientCoordViaSliceFrame = sliceOrientation * sliceScales * point + sliceInformation.Origin;
                        var patientCoordViaGridFrame  = volumeInformation.Direction * scales * point + volumeInformation.Origin;

                        if (!acceptanceTest.AcceptPositionError(volumeInformation.SopClass, patientCoordViaSliceFrame, patientCoordViaGridFrame))
                        {
                            throw new ArgumentException("The series did not conform to a regular grid.", nameof(volumeInformation));
                        }
                    }
                }
            }
        }
示例#15
0
        /// <summary>
        /// Run a test for the block manipulation methods.
        /// </summary>
        /// <param name="hVolume">The current volume pointer.</param>
        /// <param name="nOpType">The current operation type.</param>
        /// <returns></returns>
        public static void RunTest(IntPtr hVolume, XTensionActionSource nOpType)
        {
            HelperMethods.OutputMessage("VOLUME INTERACTION TESTING MODULE"
                                        , OutputMessageOptions.Level1 | OutputMessageOptions.Header);

            // GetSize() test.
            HelperMethods.OutputMessage("GetSize() Test"
                                        , OutputMessageOptions.Level2 | OutputMessageOptions.Header);

            var itemPhysicalSize = HelperMethods.GetSize(hVolume
                                                         , ItemSizeType.PhysicalSize);

            HelperMethods.OutputMessage("Volume Physical Size: "
                                        + itemPhysicalSize, OutputMessageOptions.Level3);

            var itemLogicalSize = HelperMethods.GetSize(hVolume
                                                        , ItemSizeType.LogicalSize);

            HelperMethods.OutputMessage("Volume Logical Size: "
                                        + itemLogicalSize, OutputMessageOptions.Level3);

            var itemValidDateLength = HelperMethods.GetSize(hVolume
                                                            , ItemSizeType.ValidDataLength);

            HelperMethods.OutputMessage("Volume Valid Data Length: "
                                        + itemValidDateLength, OutputMessageOptions.Level3);

            HelperMethods.OutputMessage("");

            // GetVolumeName() test.
            HelperMethods.OutputMessage("GetVolumeName() Test"
                                        , OutputMessageOptions.Level2 | OutputMessageOptions.Header);

            string type1VolumeName = HelperMethods.GetVolumeName(hVolume
                                                                 , VolumeNameType.Type1);

            HelperMethods.OutputMessage("Type 1 Volume Name: " + type1VolumeName
                                        , OutputMessageOptions.Level3);

            string type2VolumeName = HelperMethods.GetVolumeName(hVolume
                                                                 , VolumeNameType.Type2);

            HelperMethods.OutputMessage("Type 2 Volume Name: " + type2VolumeName
                                        , OutputMessageOptions.Level3);

            string type3VolumeName = HelperMethods.GetVolumeName(hVolume
                                                                 , VolumeNameType.Type3);

            HelperMethods.OutputMessage("Type 3 Volume Name: " + type3VolumeName
                                        , OutputMessageOptions.Level3);

            HelperMethods.OutputMessage("");

            // GetVolumeInformation() test.
            HelperMethods.OutputMessage("GetVolumeInformation() Test"
                                        , OutputMessageOptions.Level2 | OutputMessageOptions.Header);

            VolumeInformation volumeInformation
                = HelperMethods.GetVolumeInformation(hVolume);

            HelperMethods.OutputMessage("File System: "
                                        + volumeInformation.FileSystem, OutputMessageOptions.Level3);
            HelperMethods.OutputMessage("Bytes/Sector: "
                                        + volumeInformation.BytesPerSector, OutputMessageOptions.Level3);
            HelperMethods.OutputMessage("Sectors/Cluster: "
                                        + volumeInformation.SectorsPerCluster, OutputMessageOptions.Level3);
            HelperMethods.OutputMessage("Cluster Count: "
                                        + volumeInformation.ClusterCount, OutputMessageOptions.Level3);
            HelperMethods.OutputMessage("First Cluster Sector Number: "
                                        + volumeInformation.FirstClusterSectorNumber, OutputMessageOptions.Level3);

            HelperMethods.OutputMessage("");
        }
 static void Main(string[] args)
 {
     var volumeInfo = VolumeInformation.GetVolumeInformation("c");
 }
示例#17
0
        public FileRecord(byte[] rawBytes, int offset)
        {
            Offset = offset;
            var sig = BitConverter.ToInt32(rawBytes, 0);

            if ((sig != _fileSig) && (sig != _baadSig) && (sig != 0x0))
            {
                Logger.Fatal($"Invalid signature! 0x{sig:X}");
                return;
                //throw new Exception("Invalid signature!");
            }

            if (sig == _baadSig)
            {
                Logger.Warn($"Bad signature at offset 0x{offset:X}");
                return;
            }

            Attributes = new List <Attribute>();

            FixupOffset     = BitConverter.ToInt16(rawBytes, 2);
            FixupEntryCount = BitConverter.ToInt16(rawBytes, 4);

            LogSequenceNumber = BitConverter.ToInt64(rawBytes, 0x8);

            SequenceNumber = BitConverter.ToInt16(rawBytes, 0x10);

            ReferenceCount = BitConverter.ToInt16(rawBytes, 0x12);

            FirstAttributeOffset = BitConverter.ToInt16(rawBytes, 0x14);

            EntryFlags = (EntryFlag)BitConverter.ToInt16(rawBytes, 0x16);

            Logger.Trace($"Entry flags: {EntryFlags}");

            ActualRecordSize = BitConverter.ToInt32(rawBytes, 0x18);

            AllocatedRecordSize = BitConverter.ToInt32(rawBytes, 0x1c);

            var entryBytes = new byte[8];

            Buffer.BlockCopy(rawBytes, 0x20, entryBytes, 0, 8);

            MFTRecordToBaseRecord = new MftEntryInfo(entryBytes);

            FirstAvailableAttribueId = BitConverter.ToInt16(rawBytes, 0x28);

            EntryNumber = BitConverter.ToInt32(rawBytes, 0x2c);

            var fixupExpectedBytes = new byte[2];
            var fixupActual1       = new byte[2];
            var fixupActual2       = new byte[2];

            Buffer.BlockCopy(rawBytes, 0x30, fixupExpectedBytes, 0, 2);
            Buffer.BlockCopy(rawBytes, 0x32, fixupActual1, 0, 2);
            Buffer.BlockCopy(rawBytes, 0x34, fixupActual2, 0, 2);

            //verify this record looks ok based on fixup bytes
            //0x1FE and 0x3fe

            var expectedFixupVal = BitConverter.ToInt16(fixupExpectedBytes, 0);
            var x1FeValue        = BitConverter.ToInt16(rawBytes, 0x1FE);
            var x3FeValue        = BitConverter.ToInt16(rawBytes, 0x3FE);

            if ((x1FeValue != expectedFixupVal) &&
                ((EntryFlags & EntryFlag.FileRecordSegmentInUse) == EntryFlag.FileRecordSegmentInUse))
            {
                Logger.Warn(
                    $"FILE record at offset 0x{offset:X}! Fixup values do not match at 0x1FE. Expected: {expectedFixupVal}, actual: {x1FeValue}, EntryFlags: {EntryFlags}");
            }

            if ((x3FeValue != expectedFixupVal) &&
                ((EntryFlags & EntryFlag.FileRecordSegmentInUse) == EntryFlag.FileRecordSegmentInUse))
            {
                Logger.Warn(
                    $"FILE record at offset 0x{offset:X}! Fixup values do not match at 0x3FE. Expected: {expectedFixupVal}, actual: {x3FeValue}, EntryFlags: {EntryFlags}");
            }

            //header is done, replace fixup bytes with actual bytes
            //0x1fe and 0x3fe should contain fixup bytes

            Buffer.BlockCopy(fixupActual1, 0, rawBytes, 0x1fe, 2);
            Buffer.BlockCopy(fixupActual2, 0, rawBytes, 0x3fe, 2);

            //start attribute processing at FirstAttributeOffset

            var index = (int)FirstAttributeOffset;

            while (index < ActualRecordSize)
            {
                var attrType = BitConverter.ToInt32(rawBytes, index);

                var attrSize = BitConverter.ToInt32(rawBytes, index + 4);

//                Logger.Trace(
//                    $"ActualRecordSize: {ActualRecordSize} attrType: 0x{attrType:X}, size: {attrSize}, index: {index}, offset: 0x{offset:x}, i+o: 0x{index + offset:X}");

                if ((attrSize == 0) || (attrType == -1))
                {
                    index += 8;          //skip -1 type and 0 size

                    if (EntryFlags == 0) //this is a free record
                    {
                        break;
                    }

                    continue;
                }

                var rawAttr = new byte[attrSize];
                Buffer.BlockCopy(rawBytes, index, rawAttr, 0, attrSize);

                switch ((AttributeType)attrType)
                {
                case AttributeType.StandardInformation:
                    var si = new StandardInfo(rawAttr);
                    Attributes.Add(si);

                    SILastAccessedOn    = si.LastAccessedOn;
                    SICreatedOn         = si.CreatedOn;
                    SIRecordModifiedOn  = si.RecordModifiedOn;
                    SIContentModifiedOn = si.ContentModifiedOn;

                    break;

                case AttributeType.FileName:
                    var fi = new FileName(rawAttr);
                    Attributes.Add(fi);

                    if ((fi.FileInfo.NameType & NameTypes.Windows) == NameTypes.Windows)
                    {
                        FName = fi.FileInfo.FileName;
                    }

                    //if (fi.FileInfo.LastAccessedOn.UtcDateTime != SILastAccessedOn.UtcDateTime)
                    //{
                    FNLastAccessedOn = fi.FileInfo.LastAccessedOn;
                    //}

                    //if (fi.FileInfo.CreatedOn.UtcDateTime != SICreatedOn.UtcDateTime)
                    //{
                    FNCreatedOn = fi.FileInfo.CreatedOn;
                    //}

                    //if (fi.FileInfo.RecordModifiedOn.UtcDateTime != SIRecordModifiedOn.UtcDateTime)
                    //{
                    FNRecordModifiedOn = fi.FileInfo.RecordModifiedOn;
                    //}


                    //if (fi.FileInfo.ContentModifiedOn.UtcDateTime != SIContentModifiedOn.UtcDateTime)
                    //{
                    FNContentModifiedOn = fi.FileInfo.ContentModifiedOn;
                    //}


                    break;

                case AttributeType.Data:
                    var data = new Data(rawAttr);
                    Attributes.Add(data);
                    break;

                case AttributeType.IndexAllocation:
                    var ia = new IndexAllocation(rawAttr);
                    Attributes.Add(ia);
                    break;

                case AttributeType.IndexRoot:
                    var ir = new IndexRoot(rawAttr);
                    Attributes.Add(ir);
                    break;

                case AttributeType.Bitmap:
                    var bm = new Bitmap(rawAttr);
                    Attributes.Add(bm);
                    break;

                case AttributeType.VolumeVersionObjectId:
                    var oi = new ObjectId(rawAttr);
                    Attributes.Add(oi);
                    break;

                case AttributeType.SecurityDescriptor:
                    var sd = new SecurityDescriptor(rawAttr);
                    Attributes.Add(sd);

                    break;

                case AttributeType.VolumeName:
                    var vn = new VolumeName(rawAttr);
                    Attributes.Add(vn);
                    break;

                case AttributeType.VolumeInformation:
                    var vi = new VolumeInformation(rawAttr);
                    Attributes.Add(vi);
                    break;

                case AttributeType.LoggedUtilityStream:
                    var lus = new LoggedUtilityStream(rawAttr);
                    Attributes.Add(lus);
                    break;

                case AttributeType.ReparsePoint:
                    var rp = new ReparsePoint(rawAttr);
                    Attributes.Add(rp);
                    break;

                case AttributeType.AttributeList:
                    var al = new AttributeList(rawAttr);
                    Attributes.Add(al);
                    break;

                case AttributeType.Ea:
                    //TODO Finish this
                    var ea = new ExtendedAttribute(rawAttr);
                    Attributes.Add(ea);
                    break;

                case AttributeType.EaInformation:
                    var eai = new ExtendedAttributeInformation(rawAttr);
                    Attributes.Add(eai);
                    break;

                default:
                    Logger.Warn($"Unhandled attribute type! Add me: {(AttributeType) attrType}");
                    throw new Exception($"Add me: {(AttributeType) attrType}");
                    break;
                }

                index += attrSize;
            }

            SlackStartOffset = index;

            //rest is slack. handle here?
            Logger.Trace($"Slack starts at {index} i+o: 0x{index + offset:X}");
        }
示例#18
0
 public extern static UInt32 GetVolumeInformation(IntPtr inVolumeRef, out VolumeInformation outVolumeInfo);
示例#19
0
文件: FileRecord.cs 项目: Seabreg/MFT
        public FileRecord(byte[] rawBytes, int offset)
        {
            Offset = offset;

            var sig = BitConverter.ToInt32(rawBytes, 0);

            switch (sig)
            {
            case FileSig:
                break;

            case BaadSig:
                _logger.Debug($"Bad signature at offset 0x{offset:X}");
                IsBad = true;
                return;

            default:
                //not initialized
                _logger.Debug($"Uninitialized entry (no signature) at offset 0x{offset:X}");
                IsUninitialized = true;
                return;
            }

            _logger.Debug($"Processing FILE record at offset 0x{offset:X}");

            Attributes = new List <Attribute>();

            FixupOffset     = BitConverter.ToInt16(rawBytes, 0x4);
            FixupEntryCount = BitConverter.ToInt16(rawBytes, 0x6);

            //to build fixup info, take FixupEntryCount x 2 bytes as each are 2 bytes long
            var fixupTotalLength = FixupEntryCount * 2;

            var fixupBuffer = new byte[fixupTotalLength];

            Buffer.BlockCopy(rawBytes, FixupOffset, fixupBuffer, 0, fixupTotalLength);

            //pull this early so we can check if its free in our fix up value messages
            EntryFlags = (EntryFlag)BitConverter.ToInt16(rawBytes, 0x16);

            FixupData = new FixupData(fixupBuffer);

            FixupOk = true;

            //fixup verification
            var counter = 512;

            foreach (var bytese in FixupData.FixupActual)
            {
                //adjust the offset to where we need to check
                var fixupOffset = counter - 2;

                var expected = BitConverter.ToInt16(rawBytes, fixupOffset);
                if (expected != FixupData.FixupExpected && EntryFlags != 0x0)
                {
                    FixupOk = false;
                    _logger.Warn(
                        $"Offset: 0x{Offset:X} Entry/seq: 0x{EntryNumber:X}/0x{SequenceNumber:X} Fixup values do not match at 0x{fixupOffset:X}. Expected: 0x{FixupData.FixupExpected:X2}, actual: 0x{expected:X2}");
                }

                //replace fixup expected with actual bytes. bytese has actual replacement values in it.
                Buffer.BlockCopy(bytese, 0, rawBytes, fixupOffset, 2);

                counter += 512;
            }

            LogSequenceNumber = BitConverter.ToInt64(rawBytes, 0x8);

            SequenceNumber = BitConverter.ToUInt16(rawBytes, 0x10);

            ReferenceCount = BitConverter.ToInt16(rawBytes, 0x12);

            FirstAttributeOffset = BitConverter.ToInt16(rawBytes, 0x14);

            ActualRecordSize = BitConverter.ToInt32(rawBytes, 0x18);

            AllocatedRecordSize = BitConverter.ToInt32(rawBytes, 0x1c);

            var entryBytes = new byte[8];

            Buffer.BlockCopy(rawBytes, 0x20, entryBytes, 0, 8);

            MftRecordToBaseRecord = new MftEntryInfo(entryBytes);

            FirstAvailablAttribueId = BitConverter.ToInt16(rawBytes, 0x28);

            EntryNumber = BitConverter.ToUInt32(rawBytes, 0x2c);

            //start attribute processing at FirstAttributeOffset

            var index = (int)FirstAttributeOffset;

            while (index < ActualRecordSize)
            {
                var attrType = (AttributeType)BitConverter.ToInt32(rawBytes, index);

                var attrSize = BitConverter.ToInt32(rawBytes, index + 4);

                if (attrSize == 0 || attrType == AttributeType.EndOfAttributes)
                {
                    index += 8; //skip -1 type and 0 size

                    if (index != ActualRecordSize)
                    {
                        _logger.Warn($"Slack space found in entry/seq: 0x{EntryNumber:X}/0x{SequenceNumber:X}");
                    }

                    //TODO process slack here?
                    break;
                }

                _logger.Debug(
                    $"Found Attribute Type {attrType.ToString()} at absolute offset: 0x{index + offset:X}");

                _logger.Trace(
                    $"ActualRecordSize: 0x{ActualRecordSize:X}, size: 0x{attrSize:X}, index: 0x{index:X}");

                var rawAttr = new byte[attrSize];
                Buffer.BlockCopy(rawBytes, index, rawAttr, 0, attrSize);

                switch (attrType)
                {
                case AttributeType.StandardInformation:
                    var si = new StandardInfo(rawAttr);
                    Attributes.Add(si);
                    break;

                case AttributeType.FileName:
                    var fi = new FileName(rawAttr);
                    Attributes.Add(fi);
                    break;

                case AttributeType.Data:
                    var d = new Data(rawAttr);
                    Attributes.Add(d);
                    break;

                case AttributeType.IndexAllocation:
                    var ia = new IndexAllocation(rawAttr);
                    Attributes.Add(ia);
                    break;

                case AttributeType.IndexRoot:
                    var ir = new IndexRoot(rawAttr);
                    Attributes.Add(ir);
                    break;

                case AttributeType.Bitmap:
                    var bm = new Bitmap(rawAttr);
                    Attributes.Add(bm);
                    break;

                case AttributeType.VolumeVersionObjectId:
                    var oi = new ObjectId_(rawAttr);
                    Attributes.Add(oi);
                    break;

                case AttributeType.SecurityDescriptor:
                    var sd = new SecurityDescriptor(rawAttr);
                    Attributes.Add(sd);
                    break;

                case AttributeType.VolumeName:
                    var vn = new VolumeName(rawAttr);
                    Attributes.Add(vn);
                    break;

                case AttributeType.VolumeInformation:
                    var vi = new VolumeInformation(rawAttr);
                    Attributes.Add(vi);
                    break;

                case AttributeType.LoggedUtilityStream:
                    var lus = new LoggedUtilityStream(rawAttr);
                    Attributes.Add(lus);
                    break;

                case AttributeType.ReparsePoint:
                    try
                    {
                        var rp = new ReparsePoint(rawAttr);
                        Attributes.Add(rp);
                    }
                    catch (Exception)
                    {
                        var l = LogManager.GetLogger("ReparsePoint");

                        l.Error(
                            $"There was an error parsing a ReparsePoint in FILE record at offset 0x{Offset:X}. Please extract via --dd and --do and send to [email protected]");
                    }

                    break;

                case AttributeType.AttributeList:
                    var al = new AttributeList(rawAttr);
                    Attributes.Add(al);
                    break;

                case AttributeType.Ea:
                    var ea = new ExtendedAttribute(rawAttr);
                    Attributes.Add(ea);
                    break;

                case AttributeType.EaInformation:
                    var eai = new ExtendedAttributeInformation(rawAttr);
                    Attributes.Add(eai);
                    break;

                default:
                    throw new Exception($"Add me: {attrType} (0x{attrType:X})");
                }

                index += attrSize;
            }

            //rest is slack. handle here?
            _logger.Trace($"Slack starts at 0x{index:X} Absolute offset: 0x{index + offset:X}");
        }