/ Published in: Delphi
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
//****************************************************************************** // // UDiskAccess - A low-level disk I/O stream class (tested under Vista). // Copyright © 2009 Patryk Nusbaum. // All rights reserved. // // Last modified: 10.10.2009 // Mail: [email protected] // WWW: www.pateman.net76.net // // License // ------------------- // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Patryk Nusbaum nor the names of his contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //****************************************************************************** unit UDiskAccess; interface uses Classes; const NTDLLFile = 'ntdll.dll'; FILE_SHARE_READ = $00000001; FILE_SHARE_WRITE = $00000002; FILE_READ_ACCESS = $0001; FILE_WRITE_ACCESS = $0002; FILE_OPEN = $00000001; FILE_SYNCHRONOUS_IO_NONALERT = $00000020; SYNCHRONIZE = $00100000; OBJ_CASE_INSENSITIVE = $00000040; type LONG = Longint; LONGLONG = Int64; ULONG_PTR = Longword; USHORT = Word; DWORD = Longword; NTSTATUS = LONG; ACCESS_MASK = DWORD; PWSTR = PWideChar; LPCWSTR = PWideChar; PVOID = Pointer; { .: HANDLE :. } HANDLE = Cardinal; PHANDLE = ^HANDLE; { .: ULONG :. } ULONG = Longword; PULONG = ^ULONG; { .: IO_STATUS_BLOCK :. } _IO_STATUS_BLOCK = record Status: NTSTATUS; Information: ULONG_PTR; end; IO_STATUS_BLOCK = _IO_STATUS_BLOCK; PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK; { .: UNICODE_STRING :. } _UNICODE_STRING = record Length: USHORT; MaximumLength: USHORT; Buffer: PWSTR; end; UNICODE_STRING = _UNICODE_STRING; PUNICODE_STRING = ^UNICODE_STRING; { .: OBJECT_ATTRIBUTES :. } _OBJECT_ATTRIBUTES = record Length: ULONG; RootDirectory: HANDLE; ObjectName: PUNICODE_STRING; Attributes: ULONG; SecurityDescriptor: PVOID; SecurityQualityOfService: PVOID; end; OBJECT_ATTRIBUTES = _OBJECT_ATTRIBUTES; POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES; { .: LARGE_INTEGER :. } _LARGE_INTEGER = record case Integer of 0: (LowPart: DWORD; HighPart: LONG); 1: (QuadPart: LONGLONG); end; LARGE_INTEGER = _LARGE_INTEGER; PLARGE_INTEGER = ^LARGE_INTEGER; { .: PIO_APC_ROUTINE :. } PIO_APC_ROUTINE = procedure(ApcContext: PVOID; IoStatusBlock: PIO_STATUS_BLOCK; Reserved: ULONG); stdcall; { .: FS_INFORMATION_CLASS :. } _FSINFOCLASS = (FileFsVolumeInformation = 1, FileFsLabelInformation, // 2 FileFsSizeInformation, // 3 FileFsDeviceInformation, // 4 FileFsAttributeInformation, // 5 FileFsControlInformation, // 6 FileFsFullSizeInformation, // 7 FileFsObjectIdInformation, // 8 FileFsDriverPathInformation, // 9 FileFsVolumeFlagsInformation,// 10 FileFsMaximumInformation); FS_INFORMATION_CLASS = _FSINFOCLASS; { .: FILE_FS_SIZE_INFORMATION :. } _FILE_FS_SIZE_INFORMATION = record TotalAllocationUnits: LARGE_INTEGER; AvailableAllocationUnits: LARGE_INTEGER; SectorsPerAllocationUnit: ULONG; BytesPerSector: ULONG; end; FILE_FS_SIZE_INFORMATION = _FILE_FS_SIZE_INFORMATION; PFILE_FS_SIZE_INFORMATION = ^FILE_FS_SIZE_INFORMATION; { .: TRawDiskAccessStream :. } TRawDiskAccessStream = class sealed(TStream) private { Private declarations } FHandleRead, FHandleWrite: HANDLE; FOffset: LARGE_INTEGER; FSize: Int64; FSectorCount: Cardinal; FSectorSize: Cardinal; protected { Protected declarations } function GetSize(): Int64; override; public { Public declarations } constructor Create(const ADriveLetter: Char); destructor Destroy(); override; function SectorOffset(const ASector: Cardinal): Int64; // the "Count" parameter is ignored function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; property SectorCount: Cardinal read FSectorCount; property SectorSize: Cardinal read FSectorSize; end; procedure RtlInitUnicodeString(DestinationString: PUNICODE_STRING; SourceString: LPCWSTR); stdcall; external NTDLLFile name 'RtlInitUnicodeString'; function NtCreateFile(FileHandle: PHANDLE; DesiredAccess: ACCESS_MASK; ObjectAttributes: POBJECT_ATTRIBUTES; IoStatusBlock: PIO_STATUS_BLOCK; AllocationSize: PLARGE_INTEGER; FileAttributes: ULONG; ShareAccess: ULONG; CreateDisposition: ULONG; CreateOptions: ULONG; EaBuffer: PVOID; EaLength: ULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtCreateFile'; function NtReadFile(FileHandle: HANDLE; Event: HANDLE; ApcRoutine: PIO_APC_ROUTINE; ApcContext: PVOID; IoStatusBlock: PIO_STATUS_BLOCK; Buffer: PVOID; Length: ULONG; ByteOffset: PLARGE_INTEGER; Key: PULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtReadFile'; function NtWriteFile(FileHandle: HANDLE; Event: HANDLE; ApcRoutine: PIO_APC_ROUTINE; ApcContext: PVOID; IoStatusBlock: PIO_STATUS_BLOCK; Buffer: PVOID; Length: ULONG; ByteOffset: PLARGE_INTEGER; Key: PULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtWriteFile'; function NtClose(Handle: HANDLE): NTSTATUS; stdcall; external NTDLLFile name 'NtClose'; function NtQueryVolumeInformationFile(FileHandle: HANDLE; IoStatusBlock: PIO_STATUS_BLOCK; FsInformation: PVOID; Length: ULONG; FsInformationClass: FS_INFORMATION_CLASS): NTSTATUS; stdcall; external NTDLLFile name 'NtQueryVolumeInformationFile'; const INVALID_HANDLE_VALUE = DWORD(-1); // from Windows.pas implementation { .: NT_SUCCESS :. } function NT_SUCCESS(const Status: NTSTATUS): Boolean; begin Result := (Status >= 0); end; { .: InitializeObjectAttributes :. } procedure InitializeObjectAttributes(p: POBJECT_ATTRIBUTES; n: PUNICODE_STRING; a: ULONG; r: HANDLE; s: PVOID); begin with p^ do begin Length := SizeOf(OBJECT_ATTRIBUTES); RootDirectory := r; Attributes := a; ObjectName := n; SecurityDescriptor := s; SecurityQualityOfService := nil; end; end; { TRawDiskAccessStream } constructor TRawDiskAccessStream.Create(const ADriveLetter: Char); var DiskHandle: HANDLE; IOStatus: IO_STATUS_BLOCK; Status: NTSTATUS; ObjectAttr: OBJECT_ATTRIBUTES; DiskName: UNICODE_STRING; X: _FILE_FS_SIZE_INFORMATION; begin inherited Create(); FHandleRead := INVALID_HANDLE_VALUE; FHandleWrite := INVALID_HANDLE_VALUE; RtlInitUnicodeString(@DiskName, PChar('\DosDevices\' + ADriveLetter + ':')); InitializeObjectAttributes(@ObjectAttr, @DiskName, OBJ_CASE_INSENSITIVE, 0, nil); Status := NtCreateFile(@DiskHandle, SYNCHRONIZE or FILE_READ_ACCESS, @ObjectAttr, @IOStatus, nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, nil, 0); if (NT_SUCCESS(Status)) then FHandleRead := DiskHandle; Status := NtCreateFile(@DiskHandle, SYNCHRONIZE or FILE_WRITE_ACCESS, @ObjectAttr, @IOStatus, nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, nil, 0); if (NT_SUCCESS(Status)) then begin FHandleWrite := DiskHandle; Status := NtQueryVolumeInformationFile(FHandleRead, @IOStatus, @X, SizeOf(X), FileFsSizeInformation); FSectorCount := X.TotalAllocationUnits.QuadPart; FSectorSize := X.BytesPerSector; FSize := FSectorCount * FSectorSize; end; end; destructor TRawDiskAccessStream.Destroy; begin if (FHandleRead <> INVALID_HANDLE_VALUE) and (FHandleWrite <> INVALID_HANDLE_VALUE) then begin NtClose(FHandleRead); NtClose(FHandleWrite); FHandleRead := INVALID_HANDLE_VALUE; FHandleWrite := INVALID_HANDLE_VALUE; end; inherited Destroy(); end; function TRawDiskAccessStream.GetSize: Int64; begin Result := FSize; end; function TRawDiskAccessStream.Read(var Buffer; Count: Integer): Longint; var IOStatus: IO_STATUS_BLOCK; Status: NTSTATUS; begin Result := -1; if (FHandleRead = INVALID_HANDLE_VALUE) then exit; Status := NtReadFile(FHandleRead, 0, nil, nil, @IOStatus, @Buffer, FSectorSize, @FOffset, nil); if (NT_SUCCESS(Status)) then Result := FSectorSize else Result := 0; end; function TRawDiskAccessStream.SectorOffset(const ASector: Cardinal): Int64; begin if (ASector < FSectorCount) then Result := ASector * FSectorSize else Result := -1; end; function TRawDiskAccessStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; var X: Int64; begin case Origin of soBeginning: if (Offset <= GetSize()) then FOffset.QuadPart := Offset; soCurrent: begin X := FOffset.QuadPart + Offset; if (X <= GetSize()) then FOffset.QuadPart := X; end; soEnd: FOffset.QuadPart := GetSize() - Abs(Offset); end; Result := FOffset.QuadPart; end; function TRawDiskAccessStream.Write(const Buffer; Count: Integer): Longint; var IOStatus: IO_STATUS_BLOCK; Status: NTSTATUS; begin Result := -1; if (FHandleWrite = INVALID_HANDLE_VALUE) then exit; Status := NtWriteFile(FHandleWrite, 0, nil, nil, @IOStatus, @Buffer, FSectorSize, @FOffset, nil); if (NT_SUCCESS(Status)) then Result := FSectorSize else Result := 0; end; end.