Inheritance: IDisposable
        /// <summary>
        /// <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa365194(v=vs.85).aspx" />
        /// </summary>
        public VOLUME_DISK_EXTENTS VolumeGetVolumeDiskExtents()
        {
            // Fetch in increments of 32 bytes, as one extent (the most common case) is one extent pr. volume.
            byte[] data = DeviceIoControlHelper.InvokeIoControlUnknownSize(Handle, IOControlCode.VolumeGetVolumeDiskExtents, 32);

            // Build the VOLUME_DISK_EXTENTS structure
            VOLUME_DISK_EXTENTS res = new VOLUME_DISK_EXTENTS();

            res.NumberOfDiskExtents = BitConverter.ToUInt32(data, 0);
            res.Extents = new DISK_EXTENT[res.NumberOfDiskExtents];

            using (UnmanagedMemory dataPtr = new UnmanagedMemory(data))
            {
                // TODO: This code needs to be tested for volumes with more than one extent.
                for (int i = 0; i < res.NumberOfDiskExtents; i++)
                {
                    IntPtr currentDataPtr = new IntPtr(dataPtr.Handle.ToInt64() + 8 + i * MarshalHelper.SizeOf<DISK_EXTENT>());
                    DISK_EXTENT extent = currentDataPtr.ToStructure<DISK_EXTENT>();

                    res.Extents[i] = extent;
                }
            }

            return res;
        }
        /// <summary><see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa364563(v=vs.85).aspx"/></summary>
        public IUSN_RECORD[] FileSystemEnumUsnData()
        {
            ulong nextUsn = 0;
            const int chunkSize = 1 * 1024 * 1024;      // 1 MB chunks

            List<IUSN_RECORD> res = new List<IUSN_RECORD>();

            using (UnmanagedMemory mem = new UnmanagedMemory(chunkSize))
            {
                do
                {
                    MFT_ENUM_DATA_V0 input = new MFT_ENUM_DATA_V0();
                    input.StartFileReferenceNumber = nextUsn;
                    input.LowUsn = long.MinValue;
                    input.HighUsn = long.MaxValue;

                    int errorCode;
                    byte[] data = DeviceIoControlHelper.InvokeIoControl(Handle, IOControlCode.FsctlEnumUsnData, chunkSize, input, out errorCode);
                    Marshal.Copy(data, 0, mem, data.Length);

                    if (errorCode != 0)
                        // Exit when theres no more to do
                        break;

                    nextUsn = BitConverter.ToUInt64(data, 0);

                    int dataOffset = 8;

                    while (dataOffset < data.Length)
                    {
                        int length;
                        IUSN_RECORD rec = ParseUsnRecord(mem, dataOffset, out length);

                        if (length <= 0)
                            break;

                        res.Add(rec);

                        // Move to next record
                        dataOffset += length;
                    }

                    // Fetch next chunk
                } while (true);
            }

            return res.ToArray();
        }
        /// <summary><see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa365171(v=vs.85).aspx"/></summary>
        public DISK_GEOMETRY_EX DiskGetDriveGeometryEx()
        {
            byte[] data = DeviceIoControlHelper.InvokeIoControlUnknownSize(Handle, IOControlCode.DiskGetDriveGeometryEx, 256);

            DISK_GEOMETRY_EX res;

            using (UnmanagedMemory mem = new UnmanagedMemory(data))
            {
                res.Geometry = mem.Handle.ToStructure<DISK_GEOMETRY>();
                res.DiskSize = BitConverter.ToInt64(data, (int)MarshalHelper.SizeOf<DISK_GEOMETRY>());

                IntPtr tmpPtr = new IntPtr(mem.Handle.ToInt64() + MarshalHelper.SizeOf<DISK_GEOMETRY>() + sizeof(long));
                res.PartitionInformation = tmpPtr.ToStructure<DISK_PARTITION_INFO>();

                tmpPtr = new IntPtr(tmpPtr.ToInt64() + res.PartitionInformation.SizeOfPartitionInfo);
                res.DiskInt13Info = tmpPtr.ToStructure<DISK_EX_INT13_INFO>();
            }

            return res;
        }
        private static IUSN_RECORD ParseUsnRecord(UnmanagedMemory mem, int dataOffset, out int length)
        {
            // Get record length
            length = Marshal.ReadInt32(mem, dataOffset);
            int majorVersion = Marshal.ReadByte(mem, dataOffset + sizeof(int)) + (Marshal.ReadByte(mem, dataOffset + sizeof(int) + 1) << 8);

            if (length <= 0)
                // No more records
                return null;

            // Copy out record subset
            switch (majorVersion)
            {
                case 2:
                    USN_RECORD_V2 recordv2 = new IntPtr(mem.Handle.ToInt64() + dataOffset) .ToStructure<USN_RECORD_V2>();

                    // Parse string manually, as we cannot rely on the string to be null-terminated.
                    recordv2.FileName = Marshal.PtrToStringUni(new IntPtr(mem.Handle.ToInt64() + dataOffset + recordv2.FileNameOffset), recordv2.FileNameLength / 2);

                    return recordv2;
                case 3:
                    USN_RECORD_V3 recordv3 = new IntPtr(mem.Handle.ToInt64() + dataOffset).ToStructure<USN_RECORD_V3>();

                    // Parse string manually, as we cannot rely on the string to be null-terminated.
                    recordv3.FileName = Marshal.PtrToStringUni(new IntPtr(mem.Handle.ToInt64() + dataOffset + recordv3.FileNameOffset), recordv3.FileNameLength / 2);

                    return recordv3;
                default:
                    // Ignore
                    break;
            }

            return null;
        }
        /// <summary><see cref="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364586(v=vs.85).aspx"/></summary>
        public IUSN_RECORD[] FileSystemReadUsnJournal(long volumeJournalId, UsnJournalReasonMask reasonMask, USN firstUsn, int bytesToWaitFor = 0, int timeout = 0)
        {
            READ_USN_JOURNAL_DATA_V0 input = new READ_USN_JOURNAL_DATA_V0();

            input.StartUsn = firstUsn;
            input.UsnJournalId = volumeJournalId;
            input.ReasonMask = reasonMask;
            input.BytesToWaitFor = bytesToWaitFor;
            input.Timeout = timeout;

            int errorCode;
            byte[] data = DeviceIoControlHelper.InvokeIoControl(Handle, IOControlCode.FsctlReadUsnJournal, 1024 * 1024, input, out errorCode);

            List<IUSN_RECORD> res = new List<IUSN_RECORD>();
            using (UnmanagedMemory mem = new UnmanagedMemory(data))
            {
                int dataOffset = 8;

                while (dataOffset < data.Length)
                {
                    int length;
                    IUSN_RECORD rec = ParseUsnRecord(mem, dataOffset, out length);

                    if (length <= 0)
                        break;

                    res.Add(rec);

                    // Move to next record
                    dataOffset += length;
                }
            }

            return res.ToArray();
        }